setState is not updating state: ReactJS
I feel like I've tried everything under the sun here but must be missing something very obvious. In the addItem() function
below, I am trying to push a new item into an array and update state, but no matter what I do state just won't change from the initial array.
When I console.log newListItems the new item is included, so everything up to that point is working, it's the actual state that won't update. What am I missing?
addItem Method:
addItem(text){
var newListItems = this.state.listItems;
newListItems.push(text);
console.log(newListItems);
this.setState = ({
listItems : newListItems
});
}
Also i am not getting any error message in console.
Full Code:
import React, { Component } from 'react';
import './App.css';
class ListItem extends Component{
render(){
return (<li>{this.props.title}</li>);
}
}
class AddItem extends Component{
handleClick(){
this.props.addItem('blah');
}
render(){
return (
<div className="additem">
<input type="text" className="newitemname"/>
<span className="btn" onClick={this.handleClick.bind(this)}>Add item</span>
</div>
);
}
}
class App extends Component {
constructor(props){
super(props);
this.state = {
listItems : ['Wash the dishes','Do the laundry','Something else']
};
}
addItem(text){
var newListItems = this.state.listItems;
newListItems.push(text);
console.log(newListItems);
this.setState = ({
listItems : newListItems
});
}
render() {
return (
<div className="App">
<ul>
{this.state.listItems.map(function(item,index){
return (
<ListItem key={index} title={item} />
);
})}
</ul>
<AddItem addItem={this.addItem.bind(this)} />
</div>
);
}
}
export default App;
Solution 1:
Issue is, you are updating the state
in a wrong way, Use this:
this.setState({
listItems : newListItems
});
Instead of this:
this.setState = ({
listItems : newListItems
});
Reason:
Because setState is a function not a variable, we need to call that to update the state values.
Check this answer for more details about setState behaviour.
Other Suggestions:
When you assign any array
to any variable, variable will only get the reference of array
, means all the changes that you will perform, will directly applied on the original array
. And its not a good idea to mutate the state
variable directly, so first create the copy of the array
by using slice
, then use push
to add the item in that. Like this:
var newListItems = this.state.listItems.slice();
newListItems.push(text);
this.setState({
listItems : newListItems
});
As per DOC:
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
Check the working example:
class ListItem extends React.Component{
render(){
return (<li>{this.props.title}</li>);
}
}
class AddItem extends React.Component{
handleClick(){
this.props.addItem(this.item.value);
}
render(){
return (
<div className="additem">
<input type="text" ref={item => this.item=item} className="newitemname"/>
<span className="btn" onClick={this.handleClick.bind(this)}>Add item</span>
</div>
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.state = {
listItems : ['Wash the dishes','Do the laundry','Something else']
};
}
addItem(text){
var newListItems = this.state.listItems.slice();
newListItems.push(text);
this.setState({
listItems : newListItems
});
}
render() {
return (
<div className="App">
<ul>
{this.state.listItems.map(function(item,index){
return (
<ListItem key={index} title={item} />
);
})}
</ul>
<AddItem addItem={this.addItem.bind(this)} />
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />