I've read the ReactJS documentation about setState. Specifically, this line:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
But in my code, I do things like this:
var x = this.state.x;
x.y.z.push("foo"); // z is a list
this.setState({x:x});
This code seems to work and update state correctly. But according to the documentation, I'm breaking the rules. What's the problem with this approach? Are there performance issues? Race conditions? Will I be scolded by the Facebook dev team? Ideally, I'd like to avoid the immutability helpers and all the other craziness and keep things simple.
By manipulating state directly, you are circumventing React's state management, which really works against the React paradigm. The most significant benefit of React's setState is that it immediately triggers a re-render, which merges your DOM changes from the shadow DOM into the document DOM. So, you capture all the state changes you need in some handler, then build up the state changes you want in a literal object, then pass that to setState. This is not quite what your example above does.
So while the code sample you provided technically breaks this rule, since you are calling setState directly after mutating through a reference to a state object property, your changes are immediately being funneled through React's state management. So, everything will work as you expect. The idea is that you don't want to get in the habit of making many changes to state in this way, it's better to capture your intended state in a new literal object or array, then set it to the state once (i.e., with no previous mutations of state) through a call to setState, which will trigger a single re-render.
EDIT: To more definitively answer your question, I would add that the real concern is that a dev would directly manipulate state in many different places or methods, without calling setState, and then at some later point or other code, call setState and then wonder why their render isn't producing the results they expected. Since setState does an object merge between the known, managed state, and the literal object passed as an argument to setState, it's possible the results would not be what you would expect if you had previously manipulated state directly.
The reason is that you miss out on the callbacks associated with this.setState and they claim that you may overwrite data by just assigning straight to the object.
Also, often in OOP (I don't know how many stars JS gets...) you'll use getter and setter methods instead of directly manipulating the object. In this example via this.state.prop1 = "xyz";
I've never had react overwrite my directly set state properties. The temptation to code it this way may be write to state without re-rendering. But if you're doing that you may think about not putting those in state anyways.
If re-rendering performance is an issues checkout https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate
The main reason is that shouldComponentUpdate will not work correctly.
Related
I am learning ReactJS and I understand the benefits of functional programming. However, I am a little concerned about the actual useState approach. If we assume a variable to be changed, then why declaring it const in the first place?
I see that I can simply use let number = 4; and then render it like this <p>{number}</p>. What I cannot do however is to re-render it automatically just by changing it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so? Is there a specific reason I am missing why it was implemented the way it is? I mean why the developers have decided that if the value needs to be re-rendered upon change, then it must be a const value declared with the help of useState functionality?
I am asking this because I am suspecting I am missing some good points behind this and I would like to understand them.
The variable is declared as a const because you are not allowed to set it directly. In React the state itself is immutable. You are just allowed to change it over setState (or setNumber in your case) or with actions if you use redux.
But why is that? It may seem unnecessary cumbersome in the beginning
First of all, if your variable number changes, react has to trigger a rerender.
If the state is mutable, it requires data-binding because if the number is set, it has to update the view.
In javascript, data-binding works for simple objects, but not well for arrays. Vue.js for example, as an alternative that uses two-way data binding, had a lot of trouble in its early versions when dealing with arrays. That's why there are now only seven predefined methods to interact with arrays (which they added later to solve that problem). Vue Js Array Methods
So a simple reason to declare the state as const is that it works better with arrays. And if you watch the example you gave, setNumber(number + 1) is not that much more to write than number++. But setState(newArray) works, and newArray[i] = newElement would not work, because due to javascript limitations this cannot trigger a rerender.
Secondly, it is a nicer design concept. Think of your component as a function, that returns a view to a state. And if the state changes, you get a new view. It simplifies relationships between properties in your component. If you were allowed to change your state while rendering your component, it would create a mess.
The problem is that you're thinking of a functional component as if it was stateful. But it isn't. It's a function and once you run it, that's it.
Take this example:
function useState() {
let value = 1
function setValue(v) {
value = v
}
return [value, setValue]
}
function myFunction () {
const [value, setValue] = useState(); // <----- we use const here
return null
}
Even though we're using const, the value variable only exists within the function, once the function returns that's it. It's the same for components.
The actual value of value is stored in a whole different scope, where useEffect has access to.
Here's a deep dive on how react works internally if you're interested
React works in render cycles, i.e. some state is declared, the DOM (UI) is computed during the "render phase", and then flushed to the actual DOM during the "commit phase".
Within each cycle state is considered constant (const in JS simply means a variable can't be assigned a new value, you could just as easily declare it with let or var instead and react would work the same) but for react's purpose, state is constant during a render cycle. When it is updated via one of the state update methods, react then kicks off another render cycle (update state, compute diff, commit to DOM) and re-renders when necessary.
This process is important and the reason why external state mutations are considered anti-pattern, it goes against the react workflow and leads to buggy code or worse, UI that doesn't update as expected.
React component lifecycle
I cannot do however is to re-render it automatically just by changing
it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so?
React state updates use a process called reconciliation to figure out what changed and what to re-render. In really simplistic terms, when react state is updated it is updated with a new object reference so a shallow object comparison can more quickly detect that the component state updated.
Declaring state and doing ++number simply changes the value but not the reference, and would be considered a state mutation, an anti-pattern in react.
I am reading react.js official docs.
Here is one of them.
I am confused about this paragraph:
setState() will always lead to a re-render unless
shouldComponentUpdate() returns false. If mutable objects are being
used and conditional rendering logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
Question: Why calling setState() will avoid unnecessary re-renders if mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate()?
shouldComponentUpdate deep-dive might help you
Calling setState() will always trigger a re-render of the component(unless you have defined shouldComponentUpdate()). But keeping performance and efficiency in mind, we want the component to re-render only if the state value has actually changed.
This is where shouldComponentUpdate() lifecycle method comes into play.
In this method, a check can be done to determine whether the state value has changed. If state has changed, returns true and the component re-renders.
Mutable objects refer to Arrays, objects etc in javascript. Numbers and Strings are immutable.
Mutable object example:
const a = [1,2]; // memory address of a is 0xff456e
a.push(3); // memory address of a is 0xff456e(same)
Immutable object example:
let b = 'Hello'; // memory address of b is 0xee789e
b = 'World'; // memory address of b is 0xee789f(different because its a new object created with value 'World')
If your component is a PureComponent, then react by default will define the shouldComponentUpdate() to reduce unnecessary re-renders. But you need to use immutable objects for that to work correctly(i.e create a new array or object manually and assign to your state, else your component won't re-render correctly).
So, the point they are making is this : Don't call setState() unless the state value has actually changed if your using a normal react component without a shouldComponentUpdate() check to avoid situations like this:
this.setState({ items: [1, 2, 3] }); // re-render once
// lot of code goes here
this.setState({ items: [1, 2, 3] }); // re-render twice
Note: Although the value of items is unchanged, there is a wasteful re-render of the component caused as shown above. So, avoid setting state if there is no change of value.
I think you read that wrong.
It's a two term conditional:
IF
mutable objects are being used
AND
conditional rendering logic cannot be implemented in shouldComponentUpdate()
THEN
[you should call] setState() only when the new state differs from the previous state [so it] will avoid unnecessary re-renders.
(Alteration by me are in brackets.)
Basically, it means that it's up to you to test if you should call setState if you can't rely on React's internal tests due to technical limitations on your side.
The doc wanted to say that in the following conditions, the re-render will not happen:
If shouldComponentUpdate hook returns false.
If mutable objects are being used.
If some conditional logic is not used for re-render like force update inside shouldComponentUpdate hook.
If the effect of calling setState method only change the previous state value.
BTW, I'm still not satisfied that I couldn't make clear enough. :(:
Since the component's render method is called with each setState*, I was wondering about the performance penalty if I directly modify the state properties, call setState({}) with an empty object, and let the component fully render itself. This has an interesting side effect in the state management: The state in this case used as a mutable object rather than immutable one. Since the component will re-render with the new state values after the setState call, it will reflect the new values in the view. Of course, mutating the state is not recommended by the React team as they suggest the state should be treated as a readonly object, but nevertheless dealing with a mutable object becomes easier to do state management in some cases.
I know this is not the best practice and probably will raise some eyebrows immediately, but I cannot stop and ask for the performance impact or any other potential problems it may cause in a typical application.
Take a look at the following example for a simple demonstration: https://codesandbox.io/s/k5zy9zw8kv
So, please let me know what you think.
Thanks.
* Unless shouldComponentUpdate has been implemented within the component and returning false based on the current state and the nextState value.
PS: My personal opinion, for state management, it is better to use MobX for mid-sized to large scale applications. My question is mostly for relatively small applications. I think both react and redux are making state management unnecessarily complex than it should be. As a developer with OO background I would like to see simpler and more elegant solutions.
This is a terrible idea.
1. Officially unsupported
Direct state mutation is undocumented, unsupported and untested, so the React team can change the behaviour across versions (without testing/documenting it).
2. Breaks lifecycle methods
You break several lifecycle methods such as componentDidUpdate as prevState will not be the state before the mutation, so you give up your option of using these methods should you need them in the future.
3. Keep state shallow instead
dealing with a mutable object becomes easier to do state management
You probably get this feeling when you have deeply nested states, so modifying a nested property (eg. product.quality) requires cloning the whole object (eg. products) first.
It's recommended to keep your states as shallow as possible, both for state change simplicity and easier performance optimisation. Eg. if you state is shallow, declaring it as PureComponent will avoid it calling render when parent component re-renders with no changes to the child component.
In your example, you can move the state to Product instead:
class Product {
state = { quantity: 0 };
...
this.setState(prevState => ({ quantity: prevState.quantity + 1 }));
}
If I have a React component that requires some setup (e.g. for timers, or WebAudio API, etc), I'm having trouble deciding whether the initialization should go in constructor or componentWillMount. Is there any advantages or disadvantages to either one? It's not clear to me which one is the better place for this.
I Googled around a bit to see if anyone had discussed the differences between constructor and componentWillMount but I couldn't find anything.
EDIT: Redux and any asynchronous functions should not be part of the equation.
Normally the only thing you do in the constructor is assign your initial this.state if your component is stateful. You should not do anything else in the constructor.
componentWillMount is generally unnecessary. I would say in most cases its use is an anti-pattern. One reason people use it is for updating the state from an external source one last time before rendering but technically assigning it in the constructor is equivalent. The only minor convenience it affords is that you can setState inside it but you can’t inside the constructor.
For any side effects (data fetching or DOM manipulation) you should use componentDidMount.
If you want to call some flux action (for ajax calls) use componentWillMount or componentDidMount.
You can initialize state in constructor
I was working with React JS and I was wondering the difference between calling setState() twice to set two different state variables like this:
this.setState({fruit1: “apple”});
this.setState({fruit2: “mango”});
AND
calling setState() once and passing both state variables as JSON like this:
this.setState({fruit1: “apple”, fruit2: “mango”});
Also what is the better practice: putting state variable names in double quotes like this: this.setState({"fruit1": “apple”}) or simply ignoring the quotes?
From React documentation:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
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.
So, use this.setState({fruit1: “apple”, fruit2: “mango”});
For the second question you can look here and here
Inside a React event handler (that is, a function called from a React-based onChange={...} property and the like), multiple calls to setState are batched and the component is only re-rendered a single time. So, there's no difference between
handleClick: function() {
this.setState({fruit1: "apple"});
this.setState({fruit2: "mango"});
}
and
handleClick: function() {
this.setState({fruit1: "apple", fruit2: "mango"});
}
However, outside of the React event system, the two calls to setState will not be merged unless you wrap them in a function passed to React.addons.batchedUpdates. This is normally not something you have to worry about, but may become an issue if you start setting state in response to asynchronous events (e.g. timers, promises, callbacks, etc). For that reason, I would generally recommend the second form (with the two objects merged into one) over the first.
Here is a detailed explanation of React States and setState() method.
States (and props) are only two reasons why React re-renders/recalculate the DOM. It means if we change State, we are telling react to change the related behaviour of our app.
State is a Java-Script object with key:value pair. Now we may have many States (many key:value pairs) and let's say at certain point only one state is changing. in that case we may use this.setState() method to change only that specific state.
state = { fruit1: 'mango', fruit2: 'apple' }
let's say we want to update fruit1: 'watermelon'.
Here we can say:
this.setState( {fruit1: 'watermelon'} );
Here, we did not say anything about second state (fruit2), so react will merge changed state (fruit1) with old state (fruit2).
While, we can also say:
this.setState( {fruit1: 'watermelon' ,fruit2:'apple'} );
But it is not necessary.
Correct and Re-commanded way of setting/changing State:
From React Official docs:
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.
So here is a better way to do it:
If we are updating the counter or calculating something:
this.setState((prevState,props) => {
return {
{ counter: prevState.counter+1; }
}