score:66
the react docs for setstate
have this to say:
never mutate
this.state
directly, as callingsetstate()
afterwards may replace the mutation you made. treatthis.state
as if it were immutable.
setstate()
does not immediately mutatethis.state
but creates a pending state transition. accessingthis.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 inshouldcomponentupdate()
. if mutable objects are being used and the logic cannot be implemented inshouldcomponentupdate()
, callingsetstate()
only when the new state differs from the previous state will avoid unnecessary re-renders.
basically, if you modify this.state
directly, you create a situation where those modifications might get overwritten.
related to your extended questions 1) and 2), setstate()
is not immediate. it queues a state transition based on what it thinks is going on which may not include the direct changes to this.state
. since it's queued rather than applied immediately, it's entirely possible that something is modified in between such that your direct changes get overwritten.
if nothing else, you might be better off just considering that not directly modifying this.state
can be seen as good practice. you may know personally that your code interacts with react in such a way that these over-writes or other issues can't happen but you're creating a situation where other developers or future updates can suddenly find themselves with weird or subtle issues.
score:-2
my current understanding is based on this and this answers:
if you do not use shouldcomponentupdate
or any other lifecycle methods (like componentwillreceiveprops
, componentwillupdate
, and componentdidupdate
) where you compare the old and new props
/state
then
it is fine to mutate state
and then call setstate()
, otherwise it is not fine.
score:0
setstate trigger re rendering of the components.when we want to update state again and again we must need to setstate otherwise it doesn't work correctly.
score:1
to avoid every time to create a copy of this.state.element
you can use update with $set or $push
or many others from immutability-helper
e.g.:
import update from 'immutability-helper';
const newdata = update(mydata, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
score:4
it surprises me that non of the current answers talk about pure/memo components (react.purecomponent
or react.memo
). these components only re-render when a change in one of the props is detected.
say you mutate state directly and pass, not the value, but the over coupling object to the component below. this object still has the same reference as the previous object, meaning that pure/memo components won't re-render, even though you mutated one of the properties.
since you don't always know what type of component you are working with when importing them from libraries, this is yet another reason to stick to the non-mutating rule.
here is an example of this behaviour in action (using r.evolve
to simplify creating a copy and updating nested content):
class app extends react.component {
state = { some: { rather: { deeply: { nested: { stuff: 1 } } } } };
mutatingincrement = () => {
this.state.some.rather.deeply.nested.stuff++;
this.setstate({});
}
nonmutatingincrement = () => {
this.setstate(r.evolve(
{ some: { rather: { deeply: { nested: { stuff: n => n + 1 } } } } }
));
}
render() {
return (
<div>
normal component: <counterdisplay {...this.state} />
<br />
pure component: <purecounterdisplay {...this.state} />
<br />
<button onclick={this.mutatingincrement}>mutating increment</button>
<button onclick={this.nonmutatingincrement}>non-mutating increment</button>
</div>
);
}
}
const counterdisplay = (props) => (
<react.fragment>
counter value: {props.some.rather.deeply.nested.stuff}
</react.fragment>
);
const purecounterdisplay = react.memo(counterdisplay);
reactdom.render(<app />, document.queryselector("#root"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/ramda@0/dist/ramda.min.js"></script>
<div id="root"></div>
score:7
the simplest answer to "
why can't i directly modify a component's state:
is all about updating phase.
when we update the state of a component all it's children are going to be rendered as well. or our entire component tree rendered.
but when i say our entire component tree is rendered that doesn’t mean that the entire dom is updated. when a component is rendered we basically get a react element, so that is updating our virtual dom.
react will then look at the virtual dom, it also has a copy of the old virtual dom, that is why we shouldn’t update the state directly, so we can have two different object references in memory, we have the old virtual dom as well as the new virtual dom.
then react will figure out what is changed and based on that it will update the real dom accordingly .
hope it helps.
score:118
this answer is to provide enough information to not change/mutate the state directly in react.
react follows unidirectional data flow. meaning, the data flow inside react should and will be expected to be in a circular path.
react's data flow without flux
to make react work like this, developers made react similar to functional programming. the rule of thumb of functional programming is immutability. let me explain it loud and clear.
how does the unidirectional flow works?
states
are a data store which contains the data of a component.- the
view
of a component renders based on the state. - when the
view
needs to change something on the screen, that value should be supplied from thestore
. - to make this happen, react provides
setstate()
function which takes in anobject
of newstates
and does a compare and merge(similar toobject.assign()
) over the previous state and adds the new state to the state data store. - whenever the data in the state store changes, react will trigger an re-render with the new state which the
view
consumes and shows it on the screen.
this cycle will continue throughout the component's lifetime.
if you see the above steps, it clearly shows a lot of things are happening behind when you change the state. so, when you mutate the state directly and call setstate()
with an empty object. the previous state
will be polluted with your mutation. due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now. this will disrupt all the react's lifecycle methods.
as a result, your app will behave abnormal or even crash. most of the times, it won't affect your app because all the apps which we use for testing this are pretty small.
and another downside of mutation of objects
and arrays
in javascript is, when you assign an object or an array, you're just making a reference of that object or that array. when you mutate them, all the reference to that object or that array will be affected. react handles this in a intelligent way in the background and simply give us an api to make it work.
most common errors done when handling states in react
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// bad approach
const b = this.state.a.push(6)
this.setstate({
a: b
})
in the above example, this.state.a.push(6)
will mutate the state directly. assigning it to another variable and calling setstate
is same as what's shown below. as we mutated the state anyway, there's no point assigning it to another variable and calling setstate
with that variable.
// same as
this.state.a.push(6)
this.setstate({})
many people do this. this is so wrong. this breaks the beauty of react and is bad programming practice.
so, what's the best way to handle states in react? let me explain.
when you need to change 'something' in the existing state, first get a copy of that 'something' from the current state.
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// create a copy of this.state.a
// you can use es6's destructuring or loadash's _.clone()
const currentstatecopy = [...this.state.a]
now, mutating currentstatecopy
won't mutate the original state. do operations over currentstatecopy
and set it as the new state using setstate()
.
currentstatecopy.push(6)
this.setstate({
a: currentstatecopy
})
this is beautiful, right?
by doing this, all the references of this.state.a
won't get affected until we use setstate
. this gives you control over your code and this'll help you write elegant test and make you confident about the performance of the code in production.
to answer your question,
why can't i directly modify a component's state?
yes, you can. but, you need to face the following consequences.
- when you scale, you'll be writing unmanageable code.
- you'll lose control of
state
across components. - instead of using react, you'll be writing custom codes over react.
immutability is not necessary thing because javascript is single threaded. but, it's a good to follow practice which will help you in the long run.
ps. i've written about 10000 lines of mutable react js code. if it breaks now, i don't know where to look into because all the values are mutated somewhere. when i realized this, i started writing immutable code. trust me! that's the best thing you can do it to a product or an app.
hope this helps!
Source: stackoverflow.com
Related Query
- Why we shouldn't modify the state directly in ReactJS?
- Why can't I directly modify a component's state, really?
- React Native & Redux: Why aren't child components re-rendering on every state update?
- Why cant react set initial state based on props
- Why react `useRef` hook stores the objects in `current` property? Why cant it store directly in the ref object?
- React's setState(), data mutation for nested structures, why not modify state directly?
- React: setState with spead operator seems to modify state directly
- Can I modify state directly when using React with MobX?
- When updating state from props, why useEffect() instead of checking directly in the component?
- Why aren't the components re-rendering after a state change in React
- Why are components that are saved in parent state not updating?
- Why React Components being stored as variables or as state is bad design practice
- Why React not re-rendering components even if it's state changes after an API call
- Why cant I take a value from the current state and use that inside of my useState hook?
- React Why i cant change the display state with this.setState
- Why does React Hooks update all child components if any state changes?
- Why does calling react setState method not mutate the state immediately?
- Reactjs: how to modify dynamic child component state or props from parent?
- ReactJS: Why is passing the component initial state a prop an anti-pattern?
- Why calling setState method doesn't mutate the state immediately?
- My Redux state has changed, why doesn't React trigger a re-render?
- Why use "this.props.dispatch" rather than "store.dispatch" directly in Redux?
- React - useState - why setTimeout function does not have latest state value?
- How do I set state of sibling components easily in React?
- Why won't my nested React components render?
- Can you pass down state to child components with React Hooks?
- Redux -- why is state all in one place, even state that isn't global?
- When should ReactJS components make AJAX calls to update state from props?
- When value is assigned to components state, why console.log prints the previous state?
- Modify React Component's State using jQuery/Plain Javascript from Chrome Extension
More Query from same tag
- How to specify typescript type of a component with properties
- How to perform DIV better and deal with the css
- Return a function with arbitrary properties in typescript
- Can I programmatically pass a prop to children components in react?
- Passing React Router Link "to" parameter as props
- Fetch call in ReactJS to PHP file returning undefined
- React.js and jsx over ejs?
- '$' is not defined no-undef reactjs ajaxcall
- Can't access redux actions from props when working with react router
- How to preload i18n in advance before page load in react?
- How to handle authorization headers on API call in React?
- How to update nested objects in React state?
- React getting state
- How to properly unit test ReactDOM.render with Karma and PhantomJS?
- How else can i optimize my bundle and improve page speed?
- How to change the state object when one of the fields is an array?
- Failed to create app with create-react-app in Linux 16.04
- Material-UI Masonry: Remove space on right side
- Error: Invariant Violation: addComponentAsRefTo when using react-router
- Returning a dispatch with promises in action creators of react redux
- How to disable browser back button only for a specific component
- How to blur out an entire webpage when a button is clicked
- Can I import a png/jpg image like a component? React.js
- Getting 404 error on production server during page reload or refresh in Reactjs app.
- How can i handle the undefined error in React
- React persistent login with JWT
- How do I turn on the webview autocomplete for an login form in Ionic's capacitor in iOS?
- Unhandled Rejection (TypeError): Cannot read property 'error' of undefined
- Warning: validateDOMNesting(...): <body> cannot appear as a child of <div> in React.js
- Meteor React Subscription