I'm moving my first steps in the React world with a very basic app and I'm already struggling with the setState() function. I want to update a counter in the state each time the button gets clicked. Unfortunately, the counter always stays 0. This must be because setState() is an async function, but I thought that with the use of an arrow function the counter would have been updated. Here is the code:
import React from 'react';
import './App.css';
class App extends React.Component
{
constructor(props)
{
super(props);
this.state = { browser: "Opera", timesChanged: "0" };
this.toggleBrowser = this.toggleBrowser.bind(this);
}
toggleBrowser()
{
const newBrowser = this.state.browser == 'chrome' ? 'opera' : 'chrome' ;
this.setState(() => ({ browser: newBrowser, timeschanged: this.timeschanged }))
}
render()
{
return (
<div>
<h1>Hello World!</h1>
<p>The browser is { this.state.browser }, Changed { this.state.timesChanged }times.</p>
<button onClick={this.toggleBrowser}>Change browser!</button>
</div>
)
}
}
export default App;
I saw some users on the web use to pass the previous state to the arrow function:
this.setState((prevState) => ({ browser: newBrowser, timeschanged: prevState.timeschanged }));
But how can I pass the previous state to the setState() function ? As props, I guess, but where? And how does this change the behaviour of an async function?
Also, on a side note, as a general rule of the component's lifecycle I was told to not call setState in the render() function, but I need to define an event handler for the button which updates it, and of course it should call the setState one way or another. Since the element calls toggleBrowser, and the latter then calls setState(), didn't I break the rule by doing that, did I?
CodePudding user response:
The default syntax of setState
has the updater
Ref: https://reactjs.org/docs/react-component.html#setstate
setState(updater, [callback])
Example as per document
this.setState((state, props) => {
return {counter: state.counter props.step};
});
Both state and props received by the updater function are guaranteed to be up-to-date
The output of the updater is shallowly merged with state.
this.setState((prevState) => ({ browser: newBrowser, timeschanged: prevState.timeschanged }));
prevState
is a default parameter of existing state of the class
CodePudding user response:
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { browser: 'Opera', timesChanged: 0 };
}
toggleBrowser=()=> {
const newBrowser = this.state.browser === 'chrome' ? 'opera' : 'chrome';
this.setState((prevState) => ({
browser: newBrowser,
timesChanged: prevState.timesChanged,
}));
}
render() {
return (
<div>
<h1>Hello World!</h1>
<p>
The browser is {this.state.browser}, Changed {this.state.timesChanged}
times.
</p>
<button onClick={this.toggleBrowser}>Change browser!</button>
</div>
);
}
}
export default App;
- Your state name not equals to variable name.
- If you use arrow function you dont need to bind function in constructor
- Alwats use prevState when you are updating your state according to your current state