I am trying to change the state using setState and then call an action with new state but React dispatch the action right before the new state has been set. how do I wait for the new state and then dispatch the action ?
addressInput = e => {
this.setState({
address: e.target.value
});
this.props.filterSearch(this.state.address) //this will be called before state is set
}
this.setState provides a callback as a second parameter which is called after the state change has occurred. You should use the callback as follows
addressInput = e => {
this.setState({
address: e.target.value
},
this.props.filterSearch(this.state.address));
}
From the docs:
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.
Related
I have some code similar to this:
const [thing, setThing] = useState('');
const [value, setValue] = useState({
thing: '',
somethingElse: '',
});
//...
setThing(aThing);
setValue((prev) => ({ ...prev, thing: aThing }));
later in the code, can I guarantee that thing === value.thing? Or are the two useState setters possibly called at different times?
The setState is async function and it's possible that the the first setState will finish before the second statement if the update is not batched.
React currently will batch state updates if they're triggered from within a React-based event, like a button click or input change. It will not batch updates if they're triggered outside of a React event handler, like a setTimeout().
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
This form of setState() is also asynchronous, and multiple calls during the same cycle may be batched together. For example, if you attempt to increment an item quantity more than once in the same cycle, that will result in the equivalent of:
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the current state, we recommend using the updater function form, instead:
this.setState((state) => {
return {quantity: state.quantity + 1};
});
You can read more for state on React offical doc here.
I am currently building a TicTacToe game and would like to store my current player in state as currentPlayer. After one player moves, I update currentPlayer to the opposite player. However, when I try to log the new state to the console, it's not producing the value of the updated state.
Here is my code:
state = {
currentPlayer: 'X',
}
// This function is triggered by an onClick attribute.
// It first finds the html element and renders the currentPlayer value from state.
// It then switchs the value of currentPlayer from X to O and calls setState to update the state.
// Why does the console.log not show the updated state value?
userDidMove = () => {
document.getElementById('cell').innerHTML = this.state.currentPlayer
let nextPlayer = this.state.currentPlayer === 'X' ? 'O' : 'X'
this.setState({
currentPlayer: nextPlayer,
})
console.log ('userDidMove changed state with ',this.state.currentPlayer)
}
Any help figuring out how to get this function to return the updated state value would be great!
State changes are asynchronous. When your new state is dependent on the previous state, use the state updater function instead.
When the state changes are committed you can use the callback which will have the updated state.
this.setState((previousState) => {
const nextPlayer = previousState.currentPlayer === 'X' ? 'O' : 'X';
return {
currentPlayer: nextPlayer
}
}, () => {
// your updated state related code here
console.log('userDidMove changed state with ', this.state.currentPlayer)
});
this.setState(updatedFunc, callback);
setState is asynchronous, so the state isn’t updated immediately. You can pass a callback as the second argument to setState that will only be called when state has been updated:
this.setState(
{ currentPlayer: nextPlayer },
() => console.log(`userDidMove changed state with ${this.state.currentPlayer}`)
);
setState (React Docs):
setState(updater[, callback]) OR setState(stateChange[, callback])
Think of setState() as a request rather than an immediate command
to update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied. If you need to set the state based on the previous
state, read about the updater argument below.
NOTE: I suggest observing state using the React Dev Tools, instead of logging it.
UPDATE: This answer initially stated, incorrectly, that setState returned a promise and suggested that you could chain .then() that would be called once state was updated. I've since corrected the answer, with inspiration from #Sushanth's answer.
State changes are asynchronous. so use a function instead and the second parameter of setState function you may call the callback function to console or something else to do.
this.setState(() => ({currentPlayer: nextPlayer}), () => {
console.log('state', this.state.currentPlayer);
})
I have the following function:
onSelectDepartment = (evt) => {
const department = evt.target.value;
const course = null;
this.setState({ department, course });
this.props.onChange({ name: 'department', value: department });
this.props.onChange({ name: 'course', value: course });
if (department) this.fetch(department);
};
The question is, after the setState function get called, the render function on the component will be executed immediately or after function call is finished?
render function on the component will be executed immediately or after
function call is finished?
No one can take the guarantee of when that render function will get called, because setState is async, when we call setState means we ask react to update the ui with new state values (request to call the render method), but exactly at what time that will happen, we never know.
Have a look what react doc says about setState:
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state. This is the primary method you use to update the user
interface in response to event handlers and server responses.
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
Check this answer for more details about async behavior of setState: Why calling setState method doesn't mutate the state immediately?
If you are looking execute some piece of code only once the setState is completed you can use below format.
this.setState({
flag: true
}, ()=> {
// any code you want to execute only after the newState has taken effect.
})
This is the way to make sure your desired piece of code only runs on the new state.
I have a method that toggles a boolean value in state by copying the value then updating state:
toggleSelected = () => {
let selected = this.state.lists.selected;
selected = !selected;
this.setState({
// update state
});
};
I have another method, fired by an onClick handler, that calls toggleSelected twice:
switchList = (listName) => {
const currList = this.getCurrentList();
if(listName === currList.name) return;
this.toggleSelected(listName);
this.toggleSelected(currList);
};
However it appears that the state doesn't finish getting set from the first call by the time the second call runs; if I set a timeout on the second call, it works fine.
What's the correct way to do this?
An alternative to what #SLaks suggested, useful sometimes, is using the setState(new_state, callback) method. The callback will be run once the state is updated and "visible".
In general, setState will not change the state immediately so that it is visible in this.state. From the docs
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.
As noted in the React State/Lifecycle docs, the correct way to update state based on previous state is by passing a function into the setState() call, eg:
toggleSelected() {
this.setState((prevState, props) => {
return {
lists: {
selected : ! prevState.lists.selected
}
};
});
}
This is why you should always pass a lambda to setState(), so that it will give you the actual current state:
this.setState(state => ({ ..., selected: !state.lists.selected }))
I have the following code:
var Try = React.createClass({displayName: "Try",
getInitialState: function() {
return {
int: 'x'
};
},
ck: function() {
this.setState({int: 1});
console.log(this.state.int);
},
render: function() {
return (
React.createElement("div", {onClick: this.ck}, "Hello")
);
}
});
React.render(React.createElement(Try, null), $('body')[0]);
Try it here: http://codepen.io/rlog/pen/qdvVEK/
When I click Hello div for the first time, the log is x. Why isn't it 1 ?
According to the documentation:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
Component API # React docs
You can use optional second parameter that is a callback function to get the changed value after the setState is completed.
this.setState({int: 1}, function () {
console.log(this.state.int)
});
The second (optional) parameter is a callback function that will be executed once setState is completed and the component is re-rendered.
http://codepen.io/anon/pen/JdzONa