how does react detect changes in a component internally? - javascript

How does a component gets to know, its props are changed so that it can re-render?
Eg:
function component(props){
return <p>{this.props.childVar}</p>
}
function parentComp(){
let parentVar = 0;
setTimeout(()=>{
parentVar++;
//what happens between: after this statement is executed and till the child component is re-rendered ?
}, 1000)
return <component childVar={parentVar} />
}

One of the most important aspects of React's API is that it is declarative, and that you don't have to worry about when and how changes are being captured and handled.
Basically, each time you call render, a different tree of react elements is being made and React’s diffing algorithm will compare the old and the new tree, and will make the changes if necessary.
From a component point of view, the instance itself does not know about trees, changes and updates. After all it's just a function that returns a react element based on its paramters (props) and local state (if there is any).
You can read more about it in the Docs

In your code snippet the child component will never re-render, because what you do is just change a local variable, and child component will never know about that. So, short answer is component doesn't know is props changed.
There is two reasons for component to re-render:
this.setState was called
A parent component re-rendered

Related

Entire NextPage from NextJs is executed after Button Change. It should only executed the change. What am I doing wrong? [duplicate]

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 (
<p>Time in child:{t}</p>
);
}
});
var Main = React.createClass({
onTest: function() {
this.setState({'test':'me'});
},
render: function() {
var currentTime = new Date().getTime();
return (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});
ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
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.
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. It calls the render() method every time we call setState only except when shouldComponentUpdate returns false.
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 (
<p>Time in child:{t}</p>
);
}
});
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 (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});
ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
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
React 18 and beyond
Starting from React 18 all state updates are automatically batched. In this way, React 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 triggered from promises, setTimeout, native event handlers, or other events are not batched in React by default.
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.)
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.
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.
You could use setState() only after comparing the current state value and the new one and they are different.

React prevent re-render by using functional variant of useState

I am trying to understand the exact difference in terms of how the re-render of function component is caused in one case using plain setState V/s other case which uses functional state update
The relevant code snippet is as below
Case 1 : Causes re-render of the component
const onRemove = useCallback(
tickerToRemove => {
setWatchlist(watchlist.filter(ticker => ticker !== tickerToRemove));
},
[watchlist]
);
Case 2 : Does not cause re-render
const onRemove = useCallback(tickerToRemove => {
setWatchlist(watchlist =>
watchlist.filter(ticker => ticker !== tickerToRemove)
);
}, []);
Full example of both the use-cases can be seen on;
https://codesandbox.io/s/06c-usecallback-final-no-rerenders-bsm64?file=/src/watchlistComponent.js
https://codesandbox.io/s/06b-usecallback-usememo-ngwev?file=/src/watchlistComponent.js:961-970
UPDATE
Full article link
https://medium.com/#guptagaruda/react-hooks-understanding-component-re-renders-9708ddee9928#204b
I am a bit confused as to how the re-render of child components is prevented.
In the article it says
"Thankfully, setter function from useState hook supports a functional
variant which comes to our rescue. Instead of calling setWatchlist
with the updated watchlist array, we can instead send a function that
gets the current state as an argument"
However, I am a bit confused whether the re-rendering of child components is prevented because we use empty array (as [] does not changes between renders) V/s prevented because of using setter variant of useState hook ?
Using a functional state update or not is rather irrelevant to the question you are asking about. You appear to be asking why (1) a callback with dependency triggers a rerender versus (2) a callback with empty dependency.
The answer is quite literally very simple. In version (2) you are providing a stable callback reference from the time the component mounts that never changes, whereas in (1) the callback reference changes when the dependency does. Remember that React components rerender when state or props update (a new callback reference is a new prop reference) or when the parent component rerenders. Since the onRemove prop is updating in (1) it triggers a rerender.

What is determining the order in which these components' 'mount' functions execute?

In my code, app is the highest (functional) component. It renders piggybank (which is a class component) which renders piggychild (which is a functional component).
Each component 'console.logs' the name of itself (in the render method for class-component; simply in the function for a functional component) and has a 'mount' function (for the class-component, this means a componentDidMount method; for functional component, this means a useEffect hook whose callback's 2nd parameter is an empty array) which logs 'mount' to the console.
As you can see in the picture, PiggyBank's mount fires first, then PiggyChild's, then App's.
Is there a rule in React that governs the order in which components' 'mount' functions occur?
Or is it enough to just know that mount functions occur after all components are rendered?
useEffect has a different timing than componentDidMount. The hook that's closest to CDM is actually useLayoutEffect.
Here's a bit from ReactTraining's blog on useEffect:
They run at different times
First, let's talk about the timing of each. componentDidMount runs
after the component mounts. As the docs say, if you set state
immediately (synchronously) then React knows how to trigger an extra
render and use the second render's response as the initial UI so the
user doesn't see a flicker. Imagine you need to read the width of a
DOM element with componentDidMount and want to update state to reflect
something about the width. Imagine this sequence of events:
Component renders for the first time.
The return value of render() is used to mount new DOM.
componentDidMount fires and sets state immediately (not in an async
callback)
The state change means render() is called again and returns new JSX
which replaces the previous render.
The browser only shows the second render to avoid flicker.
It's nice that this is how it works for when we need it. But most the
time we don't need this pre-optimized approach because we're doing
asynchronous network calls and then setting state after the paint to
the screen.
componentDidMount and useEffect run after the mount. However useEffect
runs after the paint has been committed to the screen as opposed to
before. This means you would get a flicker if you needed to read from
the DOM, then synchronously set state to make new UI.
How do get the old behavior back when we need it?
useLayoutEffect was designed to have the same timing as
componentDidMount. So useLayoutEffect(fn, []) is a much closer match
to componentDidMount() than useEffect(fn, []) -- at least from a
timing standpoint.
Does that mean we should be using useLayoutEffect instead?
Probably not.
If you do want to avoid that flicker by synchronously setting state,
then use useLayoutEffect. But since those are rare cases, you'll want
to use useEffect most of the time.
I've added an example which has both Layout Effects and normal Effects in the scenario you described:
The results of running this are as follows. The main thing to note with this is that The Layout effects happen in a sensible order with the componentDidMount lifecycle hook, whereas the effects happen later.
App
PiggyBank
PiggyChild
PiggyChild LayoutEffect
PiggyBank Mount
App LayoutEffect
PiggyChild Effect
App Effect
const {useEffect, useLayoutEffect} = React;
function App(){
console.log('App');
useLayoutEffect(()=>console.log('App LayoutEffect'),[]);
useEffect(()=>console.log('App Effect'),[]);
return <PiggyBank/>;
}
class PiggyBank extends React.Component{
componentDidMount(){
console.log('PiggyBank Mount');
}
render(){
console.log('PiggyBank');
return <PiggyChild/>
}
}
function PiggyChild(){
console.log('PiggyChild');
useLayoutEffect(()=>console.log('PiggyChild LayoutEffect'),[]);
useEffect(()=>console.log('PiggyChild Effect'),[]);
return <div/>;
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
Without the LayoutEffects, the order will be:
The console.logs in the render functions, in order from parent to child, since part of rendering each component is rendering the children
Then the componentDidMount runs immediately after it finishes mounting. If there are multiple components with componentDidMounts or useLayoutEffects, these would be called in order from from child to parent as they finish mounting, but before the paint is committed (as mentioned above).
The useEffects run in order from child to parent as they finished mounting in that order, but they will always run after the componentDidMounts and useLayoutEffects due to the timing mentioned above.
const {useEffect} = React;
function App(){
console.log('App');
// useLayoutEffect(()=>console.log('App LayoutEffect'),[]);
useEffect(()=>console.log('App Effect'),[]);
return <PiggyBank/>;
}
class PiggyBank extends React.Component{
componentDidMount(){
console.log('PiggyBank Mount');
}
render(){
console.log('PiggyBank');
return <PiggyChild/>
}
}
function PiggyChild(){
console.log('PiggyChild');
// useLayoutEffect(()=>console.log('PiggyChild LayoutEffect'),[]);
useEffect(()=>console.log('PiggyChild Effect'),[]);
return <div/>;
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
Actually it makes absolute sense, and order of useEffect in hierarchy is the same as in componentDidMount.
The thing is that componentDidMount fires when all children mounted (so you would see first child event, and only then parent one) - so same order as you see.
For more details about order of componentDidMount you could see here - Order of componentDidMount in React components hierarchy

How to efficiently update state/component in React on state change [duplicate]

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 (
<p>Time in child:{t}</p>
);
}
});
var Main = React.createClass({
onTest: function() {
this.setState({'test':'me'});
},
render: function() {
var currentTime = new Date().getTime();
return (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});
ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
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.
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. It calls the render() method every time we call setState only except when shouldComponentUpdate returns false.
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 (
<p>Time in child:{t}</p>
);
}
});
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 (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});
ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
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
React 18 and beyond
Starting from React 18 all state updates are automatically batched. In this way, React 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 triggered from promises, setTimeout, native event handlers, or other events are not batched in React by default.
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.)
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.
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.
You could use setState() only after comparing the current state value and the new one and they are different.

How to handle props changes without using componentWillReceiveProps in React

I've been working on a project which is coded with React. I have a component set that I implemented many components for my own requirements. Many of these act like a composite component. For example, TextBox component which has its own label, own error message mechanism and own input filter etc. Moreover, you know, components have props to manage sth.
Everytime to update my component view (render), I use componentWillReceiveProps and I compare the props changes.
But everytime implementing the componentWillReceiveProps method is so repulsive.
Is there any way to pass props from top to down without using componentWillReceiveProps. I don't want to compare props changes manually. Is there any way to do it automatically.
When I change the props in parent, I'd like to update all views just changing the some prop values from top to down.
I'm not an react expert and performance is not my first purpose also!
One more thing that the answer is not use Redux!
I'm waiting your creative approaches and helpful ideas.
Without seeing the code for the particular thing you're working on, I may be missing something about what you're doing...
As others have commented, React will re-render your component if new props are provided, regardless of whether or not you implement componentWillReceiveProps -- the only reason to implement it is to do some kind of specific comparison or set a state based on new prop values.
From the React docs (emphasis mine):
componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
In other words, if you have a component like:
<TextBox title={"Foo"} content={"Bar"} />
That internally passes prop changes on to a couple of child components like:
class TextBox extends React.Component {
render() {
return (
<div className={'text-box'}>
<Title text={this.props.title} />
<Body text={this.props.content} />
</div>
);
}
}
Then each time new props are passed to <TextBox>, <Title> and <Body> will also get re-rendered with their new text props, and there's no reason to use componentWillReceiveProps if you're just looking to update with prop changes. React will automatically see the changes and re-render. And React handles diffing and should fairly efficiently re-render only things that have changed.
However, if you have a separate state value that needs to be set in response to props, for example, if you wanted to show a "changed" state (or whatever) on the component if the new props are different, then you could implement componentWillReceiveProps, like:
class TextBox extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.content !== nextProps.content) {
this.setState({changed: true});
}
}
render() {
const changed = this.state.changed ? 'changed' : 'unchanged';
return (
<div className={`text-box ${changed}`}>
<Title text={this.props.title} />
<Body text={this.props.content} />
</div>
);
}
}
If you're trying to prevent re-render in cases where it's unnecessary for performance, do as Andrey suggests and use shouldComponentUpdate: https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
TLDR; unless you're setting component state from props, there's likely no need to run new props through componentWillReceiveProps
UPDATE Feb 2018: in a future release, React will be deprecating componentWillReceiveProps in favor of the new getDerivedStateFromProps, more info here: https://medium.com/#baphemot/whats-new-in-react-16-3-d2c9b7b6193b
There are few suggestions:
Don't copy props into state in componentWillReceiveProps - just render directly from this.props
If your component need performance tweak (and only if there is problem with performance):
start from using shouldComponentUpdate in generic form like advised here https://facebook.github.io/react/docs/shallow-compare.html
If generic approach doesn't work for you - write custom code
The general approach, how to develop text-box-like components is to keep it stateless.Component renders props directly, and notifies parent component about changes, it don't cares about managing value.
Hope this will help
Please consider pureComponent which by defualt implements the shouldComponentUpdate inside which shallow equals is used for comparison between previous and next
try following codes:
class MyComponent extends PureComponent {...}

Categories

Resources