Difference between ComponentDidUpdate and SetState callback function - javascript

I've done some research about an error I got in my react native app
Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback..
I've done research but I still can't understand the difference between them. My theoretical idea is to remove an element from an array of objects, but when I use setState with a callback function I get the error. Can anyone point me in the right direction or show me an example of how I can use componentDidUpdate. The code example below works for now but I feel it could be improved on, and I believe that is why I'm getting my error
Delete_Task = (e) => {
this.setState(prevState => {
const tasks = prevState.daily.filter(task => task.ID !== e);
this.setState({daily: tasks});
})
}

You need to return the new state object instead of calling another setState inside the outer setState callback
change
this.setState({daily: tasks});
To
return {daily: tasks};
// OR
return {...prevState, daily:tasks}

Related

react js variable update delay - react.js [duplicate]

I'm reading Forms section of reactjs documentation and just tried this code to demonstrate onChange usage (JSBIN).
var React= require('react');
var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},
handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});
React.render(
<ControlledForm/>,
document.getElementById('mount')
);
When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?
From React's 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.
If you want a function to be executed after the state change occurs, pass it in as a callback.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.
Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.
Generally we recommend using componentDidUpdate() for such logic instead.
This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
You could try using ES7 async/await. For instance using your example:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Watch out the react lifecycle methods!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().
😂
Sometime this issue occurs with state.
In case of hooks, you should use useEffect hook, As below-
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
This saved my day, Hope will help you!!!
Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.
To guarantee an update after calling setState, there are two solutions you may pursue.
Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method
Solution 2: As mentioned in another of the above answers, pass your stuff as a callback
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:
After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below:
If you dont do either solution, i.e. if you only say this in your code:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).
If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!
There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.
Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.
async-await syntax works perfectly for something like the following...
changeStateFunction = () => {
// Some Worker..
this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));
goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.
Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.
React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.
Simply putting - this.setState({data: value}) is asynchronous in
nature that means it moves out of the Call Stack and only comes back
to the Call Stack unless it is resolved.
Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
Hence -
this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value
as it takes time to update.
To achieve the above process -
this.setState({data:value},function () {
console.log(this.state.data);
});

Unexplained behaviour from React state Change [duplicate]

I'm reading Forms section of reactjs documentation and just tried this code to demonstrate onChange usage (JSBIN).
var React= require('react');
var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},
handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});
React.render(
<ControlledForm/>,
document.getElementById('mount')
);
When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?
From React's 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.
If you want a function to be executed after the state change occurs, pass it in as a callback.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.
Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.
Generally we recommend using componentDidUpdate() for such logic instead.
This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
You could try using ES7 async/await. For instance using your example:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Watch out the react lifecycle methods!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().
😂
Sometime this issue occurs with state.
In case of hooks, you should use useEffect hook, As below-
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
This saved my day, Hope will help you!!!
Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.
To guarantee an update after calling setState, there are two solutions you may pursue.
Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method
Solution 2: As mentioned in another of the above answers, pass your stuff as a callback
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:
After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below:
If you dont do either solution, i.e. if you only say this in your code:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).
If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!
There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.
Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.
async-await syntax works perfectly for something like the following...
changeStateFunction = () => {
// Some Worker..
this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));
goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.
Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.
React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.
Simply putting - this.setState({data: value}) is asynchronous in
nature that means it moves out of the Call Stack and only comes back
to the Call Stack unless it is resolved.
Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
Hence -
this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value
as it takes time to update.
To achieve the above process -
this.setState({data:value},function () {
console.log(this.state.data);
});

What is the advantage of using componentDidUpdate over the setState callback?

Why is using componentDidUpdate more recommended over the setState callback function (optional second argument) in React components (if synchronous setState behavior is desired)?
Since setState is asynchronous, I was thinking about using the setState callback function (2nd argument) to ensure that code is executed after state has been updated, similar to then() for promises. Especially if I need a re-render in between subsequent setState calls.
However, the official React Docs say "The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead."
And that's all they say about it there, so it seems a bit vague. I was wondering if there was a more specific reason it is recommended to not use it? If I could I would ask the React people themselves.
If I want multiple setState calls to be executed sequentially, the setState callback seems like a better choice over componentDidUpdate in terms of code organization - the callback code is defined right there with the setState call. If I use componentDidUpdate I have to check if the relevant state variable changed, and define the subsequent code there, which is less easy to track. Also, variables that were defined in the function containing the setState call would be out of scope unless I put them into state too.
The following example might show when it might be tricky to use componentDidUpdate:
private functionInComponent = () => {
let someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState(
{ firstVariable: firstValue, }, //firstVariable may or may not have been changed
() => {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += someVariableBeforeSetStateCall;
this.setState({ secondVariable: secondValue });
}
);
}
vs
public componentDidUpdate(prevProps. prevState) {
if (prevState.firstVariableWasSet !== this.state.firstVariableWasSet) {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += this.state.someVariableBeforeSetStateCall;
this.setState({
secondVariable: secondValue,
firstVariableWasSet: false,
});
}
}
private functionInComponent = () => {
let someVariableBeforeSetStateCall = this.state.someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState({
firstVariable: firstValue,
someVariableBeforeSetStateCall: someVariableBeforeSetStateCall,
firstVariableWasSet: true });
//firstVariable may or may not have been changed via input,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
}
Also, apart from componentDidUpdate being generally recommended, in what cases would the setState callback be more appropriate to use?
Why is using componentDidUpdate more recommended over the setState callback function?
1. Consistent logic
When using the callback argument to setState(), you might have two separate calls to setState() in different places which both update the same state, and you'd have to remember to use the same callback in both places.
A common example is making a call to a third-party service whenever a piece of state changes:
private method1(value) {
this.setState({ value }, () => {
SomeAPI.gotNewValue(this.state.value);
});
}
private method2(newval) {
this.setState({ value }); // forgot callback?
}
This is probably a logic error, since presumably you'd want to call the service any time the value changes.
This is why componentDidUpdate() is recommended:
public componentDidUpdate(prevProps, prevState) {
if (this.state.value !== prevState.value) {
SomeAPI.gotNewValue(this.state.value);
}
}
private method1(value) {
this.setState({ value });
}
private method2(newval) {
this.setState({ value });
}
This way, the service is guaranteed to be called whenever the state updates.
Additionally, state could be updated from external code (e.g. Redux), and you won't have a chance to add a callback to those external updates.
2. Batched updates
The callback argument of setState() executes after the component is re-rendered. However, multiple calls to setState() are not guaranteed to cause multiple renders, due to batching.
Consider this component:
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: ' + this.state.value);
}
onClick = () => {
this.setState(
{ value: 7 },
() => console.log('onClick: ' + this.state.value));
this.setState(
{ value: 42 },
() => console.log('onClick: ' + this.state.value));
}
render() {
return <button onClick={this.onClick}>{this.state.value}</button>;
}
}
We have two setState() calls in the onClick() handler, each simply prints the new state value to the console.
You might expect onClick() to print the value 7 and then 42. But actually, it prints 42 twice! This is because the two setState() calls are batched together, and only cause one render to occur.
Also, we have a componentDidUpdate() which also prints the new value. Since we only have one render occurring, it is only executed once, and prints the value 42.
If you want consistency with batched updates, it's usually far easier to use componentDidMount().
2.1. When does batching occur?
It doesn't matter.
Batching is an optimization, and therefore you should never rely either on batching occurring or it not occurring. Future versions of React may perform more or less batching in different scenarios.
But, if you must know, in the current version of React (16.8.x), batching occurs in asynchronous user event handlers (e.g. onclick) and sometimes lifecycle methods if React has full control over the execution. All other contexts never use batching.
See this answer for more info: https://stackoverflow.com/a/48610973/640397
3. When is it better to use the setState callback?
When external code needs to wait for the state to be updated, you should use the setState callback instead of componentDidUpdate, and wrap it in a promise.
For example, suppose we have a Child component which looks like this:
interface IProps {
onClick: () => Promise<void>;
}
class Child extends React.Component<IProps> {
private async click() {
await this.props.onClick();
console.log('Parent notified of click');
}
render() {
return <button onClick={this.click}>click me</button>;
}
}
And we have a Parent component which must update some state when the child is clicked:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
private setClicked = (): Promise<void> => {
return new Promise((resolve) => this.setState({ clicked: true }, resolve));
}
render() {
return <Child onClick={this.setClicked} />;
}
}
In setClicked, we must create a Promise to return to the child, and the only way to do that is by passing a callback to setState.
It's not possible to create this Promise in componentDidUpdate, but even if it were, it wouldn't work properly due to batching.
Misc.
Since setState is asynchronous, I was thinking about using the setState callback function (2nd argument) to ensure that code is executed after state has been updated, similar to .then() for promises.
The callback for setState() doesn't quite work the same way as promises do, so it might be best to separate your knowledge.
Especially if I need a re-render in between subsequent setState calls.
Why would you ever need to re-render a component in between setState() calls?
The only reason I can imagine is if the parent component depends on some info from the child's DOM element, such as its width or height, and the parent sets some props on the child based on those values.
In your example, you call this.props.functionFromParentComponent(), which returns a value, which you then use to compute some state.
Firstly, derived state should be avoided, since memoization is a much better option. But even so, why not just have the parent pass the value directly down as a prop? Then you can at least compute the state value in getDerivedStateFromProps().
//firstVariable may or may not have been changed,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
These comments don't make much sense to me. The asynchronous nature of setState() doesn't imply anything about state not getting properly updated. The code should work as intended.

ReactJs setState callback not working

I have a strange behavior with the setState callback, hopefully somebody can help. The callback just isn't fired.
Here is what I do:
this.setState(
(prevState, props) => {
return { first: obj, questions: [] }
},
this.changeStateCb
);
For some reason the changeStateCb function is never being called. Same problem when I change it to:
this.setState(
(prevState, props) => {
return { first: obj, questions: [] }
},
() => console.log(this.state)
);
I just updated from v15.x to 16.2.0
Do you have a demo? I just put up a simple working example, and it seems to run just fine under React 16.2.0. Do note though that the optional callback should be used sparingly. As the docs mention, it will be executed once setState is completed and the component is re-rendered. So, a better place to do the logic in this.changeStateCb would be inside componentDidUpdate.
I just had the same issue happen to me. A simple console.log('test') in the callback wouldn't even run. It turns out I had to delete my /dist folder in the build directory. I am using Type Script and webpack and upgraded from a .net core template.
Have the same issue. The callback is not invoked. I traced it to React min code and the function to enqueueSetState does not accept the callback even though it is passed in from internal React code...so code explains why it is not called. But this used to work just fine in React 15.x - what's going on...I am trying React 16.8.4

React keep to single setState call?

Supposed that in a function, i always need to set someState, and only need to set someOtherState if condition is true.
Is it preferable to do it like this:
this.setState({ someState });
if (condition) {
this.setState({ someOtherState });
}
Or this?
if (condition) {
this.setState({ someState, someOtherState });
} else {
this.setState({ someState });
}
I know React is optimized such that calling setState in quick succession will usually not result in a re-render. But is that behavior guaranteed or should the code make such assumption?
eg. supposed it works by re-rendering on a fixed time interval, if the first setState get called right before that interval block ends, then the second setState will result in a re-render?
Why don't you use ternary operator? If condition is true, set it to new state. Otherwise, use the old one.
this.setState(prevState => ({
someState,
someOtherState: condition ? newSomeOtherState : prevState.someOtherState
}))
React batches setState calls, so doing sequential update calls should only trigger one call to render.
You can assume that it will be batched provided you are within React managed event functions (React event system). For example, if it's an AJAX call, or some other delayed function like a promise or setTimeout, they will not be batched.
EDIT
This post has a pretty good summary of the order of events in most situations. You will find the state section about halfway down, but I'll try to summarise here:
Order goes :
Updating State
ShouldComponentUpdate

ComponentWillUpdate
Render
...
If you're calling multiple functions within one of these, React is smart enough to wait until they're complete before running the batched updates.
See the React docs here for details on setState:
(Link limit: facebook.github.io)/react/docs/react-component.html#setstate
and also a discussion on batching here:
https://groups.google.com/forum/#!topic/reactjs/G6pljvpTGX0

Categories

Resources