Reputation: 3
I am trying to build a chat application with the functionality of input field which can be used as filter for chat_groups array which is in the state as chat_groups. Here is how my code looks:
constructor(props) {
super(props);
this.state = {
currUserId: "--id--",
chats: [],
chat_groups: [],
users: [],
};
}
.
.
.
<input
className="chat-group__search__input"
placeholder="Search for group..."
onChange={(ev) => {
console.log(ev.currentTarget.value);
var thatState = this.state;
thatState.chat_groups = thatState.chat_groups.map(
(gp) => {
gp["visible"] = gp.group_name
.toLowerCase()
.includes(ev.currentTarget.value);
return gp;
}
);
// getting correct state in thatState variable
this.setState(thatState);
}}
/>
// getting old state in setState callback and componentDidUpdate lifecycle
The weird problem is I am getting the correct value in thatState variable before setting state. But after setState function is called, if I try to check the state in setState callback or componentDidUpdate lifecycle, I am getting the old state only.
I tried that for keydown and change events also. So, seems to be less of an issue of event as well.
I would like to know if some issue in the code is evident or there is something that I can do to debug the issue.
Edit: After changes, my current onChange looks as below, but the issue is still there; the setState function does not seem to change the state as I can see only the old state in componentDidUpdate lifecycle and setState callback.
onChange={(ev) => {
console.log(ev.currentTarget.value);
let chat_groups = this.state.chat_groups.map((gp) => ({
...gp,
visible: gp.group_name
.toLowerCase()
.includes(ev.currentTarget.value),
}));
console.log(
"Before",
chat_groups.map((gp) => gp.visible)
);
this.setState({ chat_groups: chat_groups });
}}
Upvotes: 0
Views: 2044
Reputation: 3490
State should only be updated using the setState
method.
You are mutating the state directly in your code above - this isn't recommended. You would get unexpected results and it's not predictable.
This is how you should do it - create a new updated object and pass it to the setState
.
onChange={(ev) => {
console.log(ev.currentTarget.value);
const updatedChatGroups = this.state.chat_groups.map((gp) => {
const visible = gp.group_name.toLowerCase().includes(ev.currentTarget.value);
return {
...gp,
visible,
};
});
// Update the modified object using this.setState().
this.setState({ chat_groups: updatedChatGroups });
}}
Upvotes: 0
Reputation: 5999
The problem is that you are mutating the state
.
When you do var thatState = this.state;
the reference is still the same for both the objects. So automatically when you update thatState.chat_groups
you are updating/mutating state
as well.
Change your onChange
method to like below
onChange = ev => {
console.log(ev.currentTarget.value);
let { chat_groups } = this.state;
chat_groups = chat_groups.map(gp => ({
...gp,
visible: gp.group_name.toLowerCase().includes(ev.currentTarget.value)
}));
this.setState(state => ({
...state,
chat_groups
}));
};
//Other code
//....
//....
<input
className="chat-group__search__input"
placeholder="Search for group..."
onChange={this.onChange} />
I suspect there's one problem while checking the group_name
with the input value i.e., you are converting the group_name
to lower case
using gp.group_name.toLowerCase()
but the input value you are not converting to lower case
. This could be one issue why the visible
attribute value is not getting updated. So in the below snippet I have converted the input value also to lower case
while comparing.
Here, below is a runnable snippet with your requirement. Doing console.log
in the setState
's callback function and the state is getting updated.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currUserId: "--id--",
chats: [],
chat_groups: [{
group_name: "Group One"
}, {
group_name: "Group Two"
}],
users: []
}
}
onChange = ev => {
console.log(ev.currentTarget.value);
let {
chat_groups
} = this.state;
chat_groups = chat_groups.map(gp => ({
...gp,
visible: gp.group_name.toLowerCase().includes(ev.currentTarget.value.toLowerCase())
}));
this.setState(state => ({
...state,
chat_groups
}), () => { console.log(this.state.chat_groups); });
};
render() {
return <input
placeholder="Search for group..."
onChange={this.onChange} />
}
}
ReactDOM.render(<App />, document.getElementById("react"));
.as-console-wrapper {
max-height: 100% !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Upvotes: 1
Reputation: 148
Seems like you are trying to manipulate state directly which is a big no in React.
onChange={(ev) => {
this.setState({
chat_groups: this.state.chat_groups.map((gp) => {
gp["visible"] = gp.group_name
.toLowerCase()
.includes(ev.currentTarget.value);
return gp;
})
});
}}
Upvotes: 0
Reputation: 168
No, don't do this var thatState = this.state
it's just an object it will easily get mutate and you will not get the update as for react state change never occured.
instead do this var { chat_groups } = this.state
and then use it further and inlast set the state this.setState({ chat_groups: chat_groups })
also try to avoid the mutation as much as possible means copy the values of chat_groups
also
Upvotes: 0