Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use functional setState

I have been learning React over the past few days, looking at a few tutorials and explanations regarding the different ways in which you can write different elements. However there is one I have been most curious about - the setState function to update/override the state properties of a component.

For example, imagine I have a class with the following:

class Photos extends React.Component {     constructor() {         super()         state = {             pictures: []         }     }     componentDidMount() {       // This is where the fetch and setState will occur (see below)    }      render() {        return {           <div className="container">              {this.state.pictures}           </div>        }     } } 

This example sees me fetch images from an API.

Given that I have performed my fetch, map and return for this function - I will then update the pictures: [] state array with the results gained in the API call.

My question stems from the differing methods I have seen regarding how to update/override the pictures state property.

I have seen it written in 2 varying ways:

1) This seems to be a very simple and easy to read method

this.setState({pictures: pics})

2) This is more complex but I have see it described as a more safe method

this.setState(prevState => ({    pictures: prevState.pictures.concat(pics) })) 

Could somebody please explain the merits of using either? I want to be consistent with code in the future, dealing with props and states etc, so the most versatile method would of course be preferred.

like image 493
physicsboy Avatar asked Jan 11 '18 14:01

physicsboy


People also ask

Why is it recommended to use the setState function instead of directly updating our application state like?

Passing in a function into setState instead of an object will give you a reliable value for your component's state and props .

Why should we pass function to setState?

This happens because React's setState() is an async operation, so if sometimes you wanna perform multiple actions at once, you can't really rely on the state variable which does not get updated fast enough at some point.

What does the setState () function do?

setState() allows you to change state in a React class component. To change the state of a function component, you use the useState() hook. Learn more about it here. setState() enqueues change to the component state and tell React that this component and its children need to be re-rendered with the updated state.

Can I use setState in functional component?

useState returns the current state and a function to update it. But this function updates the value in an asynchronous way. That means by calling that function, the variable is not going to change immediately.


2 Answers

First things first, in your case the two syntaxes are entirely different, what you might be looking for is the difference between

this.setState({pictures: this.state.picture.concat(pics)}) 

and

this.setState(prevState => ({    pictures: prevState.pictures.concat(pics) })) 

To understand why the second method is a preferred one,you need to understand what React does with setState() internally.

React will first merge the object you passed to setState() into the current state. Then it will start that reconciliation thing. Because of the calling setState() might not immediately update your state.

React may batch multiple setState() calls into a single update for better performance.

Consider the simple case, to understand this, in your function you might call setState() more than once like:

myFunction = () => {     ...    this.setState({pictures: this.state.picture.concat(pics1)})    this.setState({pictures: this.state.picture.concat(pics1)})    this.setState({pictures: this.state.picture.concat(pics1)})     ... } 

which isn't a valid use case in a simple app but as the app gets complex, multiple setState() calls may be happening from multiple places, doing the same thing.

So now to perform an efficient update, React does the batching thing by extracting all the objects passed to each setState() call, merges them together to form a single object, then uses that single object to do setState(). According to the setState() documentation:

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}; }); 

For more detail, see:

  • State and Lifecycle guide
  • In depth: When and why are setState() calls batched?
  • In depth: Why isn’t this.state updated immediately?

setState() - Other APIs - React.Component – React.

So if any of the objects contains the same key, the value of the key of the last object with same key is stored. And hence the update only happens once with the last value.

Demo Codesandbox

like image 102
Shubham Khatri Avatar answered Oct 11 '22 07:10

Shubham Khatri


TL;DR: Functional setState is mainly helpful to expect correct state update when multiple setStates are triggered in a short interval of time where as conventional setState does reconciliation and could lead to unexpected state.

Please check this wonderful article on Functional setState to get a clear understanding

And here is the WORKING DEMO

Firstly, you are trying to do different things in each of your cases, you are assigning pics in one and concatenating pics in another.

Lets say this.state.pictures contain [1, 2, 3] and pics contain [4, 5, 6]

this.setState({pictures: pics})

In the above case this.state.pictures will contain the pics i.e this.state.pictures = [4, 5, 6]

this.setState(prevState => ({    pictures: prevState.pictures.concat(pics) })) 

Whereas in the above snippet, this.state.pictures will contain the previous pictures + pics i.e this.state.pictures = [1, 2, 3, 4, 5, 6]

Either way you can tweak it to meet your API needs, if it's like a paginated response the you are better off concatenating the results on each request, else if you get the whole array each time just assign the whole array.

Conventional setState VS Functional setState

Now, your question mainly is probably whether to use setState with an object or with a function.

People use both of them for different reasons, now the functional setState is said to be safe because React does something called a reconciliation process where in it merges multiple objects inside setState when triggered frequently or concurrently and applies the changes in one shot, kind of like a batch process. This could potentially lead to some unexpected results in scenarios like below.

EXAMPLE:

increaseScoreBy3 () {  this.setState({score : this.state.score + 1});  this.setState({score : this.state.score + 1});  this.setState({score : this.state.score + 1}); } 

As you would expect the score to be incremented by 3 but what React does is merge all the objects and update the score only once i.e incrementing it by 1

This is one of the cases where functional callback shines because reconciliation does not happen on functions and executing the below code will do things as expected i.e update score by 3

increaseScoreBy3 () {  this.setState(prevState => ({ score: prevState.score + 1 }));  this.setState(prevState => ({ score: prevState.score + 1 }));  this.setState(prevState => ({ score: prevState.score + 1 })); } 
like image 33
Nandu Kalidindi Avatar answered Oct 11 '22 06:10

Nandu Kalidindi