ChatGPT解决这个技术问题 Extra ChatGPT

ReactJS - Does render get called any time "setState" is called?

Does React re-render all components and sub components every time setState() is called?

If so, why? I thought the idea was that React only rendered as little as needed - when state changed.

In the following simple example, both classes render again when the text is clicked, despite the fact that the state doesn't change on subsequent clicks, as the onClick handler always sets the state to the same value:

this.setState({'test':'me'});

I would've expected that renders would only happen if state data had changed.

Here's the code of the example, as a JS Fiddle, and embedded snippet:

var TimeInChild = React.createClass({ render: function() { var t = new Date().getTime(); return (

Time in child:{t}

); } }); var Main = React.createClass({ onTest: function() { this.setState({'test':'me'}); }, render: function() { var currentTime = new Date().getTime(); return (

Time in main:{currentTime}

Click me to update time

); } }); ReactDOM.render(
, document.body);

I had the same issue, I don't know the exact solution. But I had cleaned up the unwanted codes from the component it started working as usual.
I would like to point out that in your example - because how your element is designed does not solely rely on a unique state - calling setState() even with dummy data does cause the element to render differently so I would say yes. Absolutely it should try to re-render your object when something might have changed because otherwise your demo - assuming it was the intended behaviour - wouldn't work!
You may be right @TadhgMcDonald-Jensen - but from my understanding, React would've rendered it the first time (since state changes from nothing to something that first time), then never had to render again. I was wrong though, of course - as it looks like React requires you to write your own shouldComponentUpdate method, which I assumed a simple version of it must already be included in React itself. Sounds like the default version included in react simply returns true - which forces the component to re-render every single time.
Yes but it only needs to re-render in the virtual DOM then it only changes the actual DOM if the component is rendered differently. Updates to the virtual DOM are usually negligible (at least compared to modifying things on the actual screen) so calling render every time it might need to update then seeing that no change has happened not very expensive and safer than assuming it should render the same.

A
Ankit

Does React re-render all components and sub-components every time setState is called?

By default - yes.

There is a method boolean shouldComponentUpdate(object nextProps, object nextState), each component has this method and it's responsible to determine "should component update (run render function)?" every time you change state or pass new props from parent component.

You can write your own implementation of shouldComponentUpdate method for your component, but default implementation always returns true - meaning always re-run render function.

Quote from official docs http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

By default, shouldComponentUpdate always returns true to prevent subtle bugs when the state is mutated in place, but if you are careful to always treat the state as immutable and to read-only from props and state in render() then you can override shouldComponentUpdate with an implementation that compares the old props and state to their replacements.

Next part of your question:

If so, why? I thought the idea was that React only rendered as little as needed - when the state changed.

There are two steps of what we may call "render":

Virtual DOM renders: when render method is called it returns a new virtual dom structure of the component. As I mentioned before, this render method is called always when you call setState(), because shouldComponentUpdate always returns true by default. So, by default, there is no optimization here in React. Native DOM renders: React changes real DOM nodes in your browser only if they were changed in the Virtual DOM and as little as needed - this is that great React's feature which optimizes real DOM mutation and makes React fast.


Great explanation! And the one thing that really makes React still shine in this case is that it doesn't change the real DOM unless the virtual DOM has been changed. So if my render function returns the same thing 2 times in a row, the real DOM doesn't get changed at all... Thanks!
I think @tungd is right - see his answer below. It is also a matter of a parent-child relation between components. For example if TimeInChild is a sibling of Main then its render() will not be called unnecessarily.
If your state contains a relatively large javascript object (~1k total properties), which is rendered as a large tree of components (~100 total)... should you let the render functions construct the virtual dom, or should you, before setting the state, compare the new state to the old manually and only call setState if you detect there's a difference? If so, how to do this best - compare the json strings, construct and compare object hashes,...?
@Petr , but even though react reconstruct the virtual dom, if the old virtual dom is the same as new virtual dom, the browser dom will not be touched , isn't it?
Also, look into using React.PureComponent (reactjs.org/docs/react-api.html#reactpurecomponent). It only updates (re-renders) if the component's state or props have actually changed. Beware, however, that the comparison is shallow.
A
Ankit

No, React doesn't render everything when the state changes.

Whenever a component is dirty (its state changed), that component and its children are re-rendered. This, to some extent, is to re-render as little as possible. The only time when render isn't called is when some branch is moved to another root, where theoretically we don't need to re-render anything. In your example, TimeInChild is a child component of Main, so it also gets re-rendered when the state of Main changes.

React doesn't compare state data. When setState is called, it marks the component as dirty (which means it needs to be re-rendered). The important thing to note is that although render method of the component is called, the real DOM is only updated if the output is different from the current DOM tree (a.k.a diffing between the Virtual DOM tree and document's DOM tree). In your example, even though the state data hasn't changed, the time of last change did, making Virtual DOM different from the document's DOM, hence why the HTML is updated.


Yes, this is the correct answer. As an experiment modify the last line as React.renderComponent(
, document.body); and remove from the body of render() of the Main component. The render() of TimeInChild will not be called by default because it is not a child of Main any more.
Thanks, stuff like this is a little bit tricky, that's why the React authors recommended that the render() method should be "pure" - independent of outer state.
@tungd, what does it mean some branch is moved to another root? What do you call branch? What do you call root?
I really prefer the answer marked as the correct one. I understand your interpretation of 'rendering' on the native, 'real', side... but if you look at it from the react-native side you must say that it renders again. Luckily it is smart enough to determine what really changed and only update these things. This answer might be confusing for new users, first you say no, and then you explain that things do get rendered...
@tungd can u please explain Green's question what does it mean some branch is moved to another root? What do you call branch? What do you call root?
B
Brad Parks

Even though it's stated in many of the other answers here, the component should either:

implement shouldComponentUpdate to render only when state or properties change

switch to extending a PureComponent, which already implements a shouldComponentUpdate method internally for shallow comparisons.

Here's an example that uses shouldComponentUpdate, which works only for this simple use case and demonstration purposes. When this is used, the component no longer re-renders itself on each click, and is rendered when first displayed, and after it's been clicked once.

var TimeInChild = React.createClass({ render: function() { var t = new Date().getTime(); return (

Time in child:{t}

); } }); var Main = React.createClass({ onTest: function() { this.setState({'test':'me'}); }, shouldComponentUpdate: function(nextProps, nextState) { if (this.state == null) return true; if (this.state.test == nextState.test) return false; return true; }, render: function() { var currentTime = new Date().getTime(); return (

Time in main:{currentTime}

Click me to update time

); } }); ReactDOM.render(
, document.body);


P
Penny Liu

Yes. It calls the render() method every time we call setState only except when shouldComponentUpdate returns false.


Please be more elaborative
D
Danny Harding

It seems that the accepted answers are no longer the case when using React hooks (with primitive values, see comments on this answer for details). You can see in this code sandbox that the class component is rerendered when the state is set to the same value, while in the function component, setting the state to the same value doesn't cause a rerender.

https://codesandbox.io/s/still-wave-wouk2?file=/src/App.js


That's because in your example, you're passing a primitive value to the useState hook and complex object to the setState method. Try passing an object or an array to the useState hook to see re-renders in function component, because you're changing object references. Just to clarify your example, but you're right in that using useState hook with primitive values can prevent re-rendering.
@MichalSvorc It's true that if we used an object in useState then it would also rerender, but the point is that it's impossible to use a simple primitive with the class component's setState function. The example is only showing the difference between using the simplest form of class component state and the simplest form of useState, and how the hooks version is (ever so slightly) more performant 👍
sure, I just added an explanation of why is the useState not re-rendering in your example. Someone might pass an object to the useState (which is legal) thinking that the hook will automatically prevent re-render, because you stated that "the accepted answers are no longer the case when using React hooks". Someone might not spot the difference in using a primitive and an object reference right away.
That makes sense, thanks. I added a minor clarification to the answer
Z
Zoltán Krizsán

Another reason for "lost update" can be the next:

If the static getDerivedStateFromProps is defined then it is rerun in every update process according to official documentation https://reactjs.org/docs/react-component.html#updating.

so if that state value comes from props at the beginning it is overwrite in every update.

If it is the problem then U can avoid setting the state during update, you should check the state parameter value like this

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

Another solution is add a initialized property to state, and set it up in the first time (if the state is initialized to non null value.)


l
lifeisfoo

React 18 and beyond

Starting from React 18 all state updates are automatically batched to groups multiple state updates into a single re-render for better performance.

So when you update your state, React always try to batch these updates in a group update, causing fewer render than setState calls. The behaviour is the same when using hooks.

You can read the very long explanation in the Automatic batching for React 18 announcement.

React 17 and below

In React 17 and below, only updates inside React event handlers are batched. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default.


S
Singhi John

Not All Components.

the state in component looks like the source of the waterfall of state of the whole APP.

So the change happens from where the setState called. The tree of renders then get called from there. If you've used pure component, the render will be skipped.


b
boaz levinson

Regardless of the well explained answers here, there may be other reasons why you don't see the change you expect post changing the props or state:

Watch out for any event.preventDefault(); in the code where you want to re-render by a state \ props change, as it will cancel any cancelable event following this statement.


C
Ciocoiu Ionut Marius

You could use setState() only after comparing the current state value and the new one and they are different.


Great comment, lacking in detail for an answer though
You use setState() in order to update a local state (variables inside state) of a component. Use setState() only if the variable from state was changed and will make sense that the component renders again. You may use useRef(), if you don't want to render again the component, or you may also render again the component by using some variable and dispatch some action to force rendering the component again. In some cases is recommended to just only use let or const for managing variables. Please take also in consideration useMemo, to avoid componentes rendering again if is not required.
There is an edit button below your question that allows you to add detail to your answer after posting

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now