render react component when prop changes - javascript

how to force render our component when props changes?
how to force render parent component when child component's props changes?
i searched a lot but all solutions are deprecated in new React version.
in my any pages (Route exact path="/post/:id" component={post}) for example (siteUrl/post/1) i am getting (:id) from props (this.props.match.params) and that works.
but when i am in single page component (Post) and this Route (siteUrl/post/1) when i am passing new Props to this component (:id).
props will changes but single component and parent component Will not re render...

You may be using componentDidMount method.
componentDidMount() is invoked immediately after a component is
mounted (inserted into the tree). Initialization that requires DOM
nodes should go here. If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.
but you need to use componentDidUpdate.
componentDidUpdate() is invoked immediately after updating occurs.
This method is not called for the initial render.
You can also use state and other React features without writing a class.
read more: https://reactjs.org/docs/hooks-effect.html

To make both parent and child re-render you need to path prop from parent to it's child.
// parent using
<Parent someProp={{someVal}} />
// parent render:
render() {
const { someProp } = this.props
<Child someProp={{someProp}} />
}
this will surely re-render both components, unless you stated another logic in componentShouldUpdate
in your case Router looks like a parent for Parent so you should only path :id as a prop.
Make sure Router is at the top level, right under the App

Important is ,that you initialise the someVal of the child first in the constructor
public static getDerivedStateFromProps(
nextProps,
nextState
) {
let { someVal } = nextProps;
if (nextState.someVal !== someVal) {
return {
initialVal,
someVal: someVal
};
}
return null;
}
After it will rerender on prop changes because the state changes

Related

How does React update a component and its children after a state change?

I am watching Paul O Shannessy - Building React From Scratch
And I understand the mounting process very well but I have hard day trying to understand how React update a component and its children
The reconciler controls the update process by this method:
function receiveComponent(component, element) {
let prevElement = component._currentElement;
if (prevElement === element) {
return;
}
component.receiveComponent(element);
}
Component.receiveComponent
receiveComponent(nextElement) {
this.updateComponent(this._currentElement, nextElement);
}
and this is the Component.updateComponent method:
updateComponent(prevElement, nextElement) {
if (prevElement !== nextElement) {
// React would call componentWillReceiveProps here
}
// React would call componentWillUpdate here
// Update instance data
this._currentElement = nextElement;
this.props = nextElement.props;
this.state = this._pendingState;
this._pendingState = null;
let prevRenderedElement = this._renderedComponent._currentElement;
let nextRenderedElement = this.render();
if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) {
Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
}
}
This is the part of the code that updates the component after state change, and i assume that it should update the children too, but i can't understand how this code achieves that, in the mounting process React instantiate components to dive deeper in the tree but this doesn't happen here, we need to find the first HTML element then we can change our strategy and update that HTML element in another place in the code, and I can't find any way to find any HTML elements this way.
Finding the first HTML is the way to stop this endless recursion and logically this is what I expect from the code, to stop recursion the same way in the mounting process, but in mounting, this demanded component instantiation so we can delegate to the reconciler that will discover that we are dealing with a wrapper instance of an HTML element not a wrapper instance of a custom component then React can place that HTML element in the DOM.
I can't understand how the code works in the update process. this code as I see won't dive deeper in the tree and I think won't update the children and can't let React find the first HTML element so React can update the DOM element, isn't it?
This is the code repo on Github
I created a codesandbox to dig in
Here is the codesandbox I created
and here's a short recording of me opening the debugger and seeing the call stack.
How it works
Starting from where you left off, Component.updateComponent:
updateComponent(prevElement, nextElement) {
//...
if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) {
Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
//...
in the Component.updateComponent method Reconciler.receiveComponent is called which calls component.receiveComponent(element);
Now, this component refers to this._renderedComponent and is not an instance of Component but of DOMComponentWrapper
and here's the receiveComponent method of DOMComponentWrapper:
receiveComponent(nextElement) {
this.updateComponent(this._currentElement, nextElement);
}
updateComponent(prevElement, nextElement) {
// debugger;
this._currentElement = nextElement;
this._updateDOMProperties(prevElement.props, nextElement.props);
this._updateDOMChildren(prevElement.props, nextElement.props);
}
Then _updateDOMChildren ends up calling the children render method.
here's a call stack from the codesandbox I created to dig in.
How do we end up in DOMComponentWrapper
in the Component's mountComponent method we have:
let renderedComponent = instantiateComponent(renderedElement);
this._renderedComponent = renderedComponent;
and in instantiateComponent we have:
let type = element.type;
let wrapperInstance;
if (typeof type === 'string') {
wrapperInstance = HostComponent.construct(element);
} else if (typeof type === 'function') {
wrapperInstance = new element.type(element.props);
wrapperInstance._construct(element);
} else if (typeof element === 'string' || typeof element === 'number') {
wrapperInstance = HostComponent.constructTextComponent(element);
}
return wrapperInstance;
HostComponent is being injected with DOMComponentWrapper in dilithium.js main file:
HostComponent.inject(DOMComponentWrapper);
HostComponent is only a kind of proxy meant to invert control and allow different Hosts in React.
here's the inject method:
function inject(impl) {
implementation = impl;
}
and the construct method:
function construct(element) {
assert(implementation);
return new implementation(element);
}
When we have no DOMComponentWrapper
If we are updating a chain of Non Host Components like:
const Child = <div>Hello</div>
const Parent = () => <Child />
How does Child get rendered from an update to Parent?
the Parent Component has the following:
_renderedComponent which is an instance of Child(which is also a Component)
renderedComponent has an instance of Child because it gets the type of the "root" Element (the one returned by the render method)
so Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement) will be calling component.receiveComponent(element) of the Child which in turn calls this.updateComponent(this._currentElement, nextElement); (of Child) which calls it's render method (let nextRenderedElement = this.render();)
React completely copy the actual DOM and create the virtual DOM in javascript. In our application whenever we update any of the data that ends up being rendered in our components, React does not rerender the entire DOM. It only affects the thing that matters. So react actually copies the virtual DOM again. This time it applies the changes to the data that got updated.
It will make the change in the red component and then it will compare this virtual DOM to the old DOM. It will see the different part. Then it will apply the DOM changes only to that different component.
The updating phase starts if props or the state changes. If the data at the top level changes:
If it is passing that data down to its children, all the children are going to be rerendered. If the state of the component at the mid-level gets changed:
This time only its children will get rerendered. React will rerender any part of the tree below that node. Because the data that generates the children components' view actually sits at the parent component(mid-level one). But anything above it, the parent or the siblings will not rerender. because data does not affect them. this concept is called Unidirectional Data Flow.
You can see in action in chrome browser. chose the rendering and then enable the painting flushing option
If you make any change on the page, you will see that updated components will be flashed.
UPDATING PHASE
componentWillReceiveProps method is invoked first in the component lifecycle's updating phase. It is called when a component receives new properties from its parent component. With this method we compare the current component's properties using the this.props object with the next component's properties
using the nextElement.props object. Based on this comparison, we can choose to update the component's state using the this.setState() function, which will NOT trigger
an additional render in this scenario.
Note that no matter how many times you call this.setState() in the componentWillReceiveProps() method, it won't trigger any additional renders of that component. React does an internal optimization where it batches the state updates together.
shouldComponentUpdated dictates if the components should rerender or not. By default, all class components will rerender whenever the props they receive or their state change. this method can prevent the default behavior by returning False. In this method, existing props and state values get compared with the next props and state values and return boolean to let React know whether the component should update or not. this method is for performance optimization. If it returns False componentWillUpdate(), render() and componentDidUpdate() wont get called.
The componentWillUpdate() method is called immediately before React updates the DOM. It gets two arguments: nextProps and nextState. You can use these arguments to prepare for the DOM update. However, you cannot use this.setState() in the componentWillUpdate() method.
After calling the componentWillUpdate() method, React invokes the render() method that performs the DOM update. Then, the componentDidUpdate() method is called.
The componentDidUpdate() method is called immediately after React updates the DOM. It gets these two arguments: prevProps and prevState. We use this method to interact with the updated DOM or perform any post-render operations. For example, in a counter example, counter number is increased in componentDidUpdate.
After componentDidUpdate() is called, the updating cycle ends. A new cycle is started when a component's state is updated or a parent component passes new properties. Or when you call the forceUpdate() method, it triggers a new updating cycle, but skips the shouldComponentUpdate() method (this method is for optimization) on a component that
triggered the update. However, shouldComponentUpdate() is called on all the child components as per the usual updating phase. Try to avoid using the forceUpdate() method as much as possible; this will promote your application's maintainability
Another answer might be the structure of the Fiber tree. During execution, react renders a ReactComponent into an object made out of ReactNodes and props. These ReactNodes are assembled into a FiberNode tree (which might be the in memory representation of the virutal dom?).
In the FiberNode tree, depending on the traversal algorithm (children first, sibling first, etc), React always has a single "next" node to continue. So, React will dive deeper into the tree, and update FiberNodes, as it goes along.
If we take the same example,
function App() {
return <div>
<Parent>
<Child01/>
<Child01/>
</Parent>
<Child03/>
</div>
}
function Parent({children}) {
const [state, setState] = useState(0);
return <div>
<button onClick={x => x+1)>click</button>
<Child02 />
{children}
</div>
}
Which React will transform into this FiberNode tree:
node01 = { type: App, return: null, child: node02, sibling: null }
node02 = { type: 'div', return: node01, child: node03, sibling: null }
node03 = { type: Parent, return: node02, child: node05(?), sibling: node04 }
node04 = { type: Child03, return: node02, child: null, sibling: null }
node05 = { type: Child01, return: node03, child: null, sibling: node06 }
node06 = { type: Child01, return: node03, child: null, sibling: null }
// Parent will spawn its own FiberTree,
node10 = { type: 'div', return: node02, child: node11, sibling: null }
node11 = { type: 'button', return: node10, child: null, sibling: node12 }
node12 = { type: Child02, return: node10, child: null, sibling: node05 }
I might have missed something (ie. node03's child might be node10), but the idea is this - React always have a single node (the 'next' node) to render when it traverses the fiber tree.
I think React not re-render parent component first instead of that, React re-render child component first.
Example: A (parent) -> B (child) -> C (child of B)
When A update state C (re-render) -> B -> A
Hey Consider using a Tree data structure for your need, ReactJs follows a unidirectional manner of Updating the state i.e. As soon as the there is a Change in the parent state then all the children which are passed on the props that are residing in the Parent Component are updated once and for all!
Consider using something known as Depth First Search as an algo option which will find you the Node that connects to the parent and once you reach that node , you check for the state and if there is a deviation from the state variables that are shared by the parent you can update them!
Note : This may all seem a bit theoretical but if you could do something remotely close to this thing you will have created a way to update components just how react does!
I found out experimentally that React will only re-render elements if it have to, which is always, except for {children} and React.memo().
Using children correctly, together with batched dom updates makes a very efficient and smooth user experience.
consider this case:
function App() {
return <div>
<Parent>
<Child01/>
<Child01/>
</Parent>
<Child03/>
</div>
}
function Parent({children}) {
const [state, setState] = useState(0);
return <div>
<button onClick={x => x+1)>click</button>
<Child02 />
{children}
</div>
}
when clicking on the button, you will get the following:
- button click
- setState(...), add Parent to dirty list
- start re-rendering all dirty nodes
- Parent rerenders
- Child02 rerenders
- DONE
Note that
Parent (app) and sibling (Child03) nodes will not get re-rendered, or you'll end up with a re-render recursion.
Parent is re-rendered because its state has changed, so its output has to be recalculated.
{children} have not been affected by this change, so it stays the same. (unless a context is involved, but that's a different mechanism).
finally, <Child02 /> has been marked dirty, because that part of the virtual dom has been touched. While it's trivial for us to see it was not effected, the only way React could verify it is by comparing props, which is not done by default!
the only way to prevent Child02 from rendering is wrapping it with React.memo, which might be slower than just re-rendring it.

React setState of Parent component without rerendering the Child

I have a parent Component with a state variable that gets changed by one of its child components upon interaction. The parent then also contains some more components based on the data in the state variable.
The problem is that the child component rerenders when the state of its parent changes because the reference to the setState function changes. But when I use useCallback (as suggested here), the state of my parent just does not update at all.
This is my current setup:
function ArtistGraphContainer() {
const [artistPopUps, setArtistPopUps] = useState([])
const addArtistPopUp = useCallback(
(artistGeniusId, xPos, yPos) => {
setArtistPopUps([{artistGeniusId, xPos, yPos}].concat(artistPopUps))
},
[],
)
return (
<div className='artist-graph-container'>
<ArtistGraph addArtistPopUp={addArtistPopUp} key={1}></ArtistGraph>
{artistPopUps.map((popUp) => {
<ArtistPopUp
artistGeniusId={popUp.artistGeniusId}
xPos={popUp.xPos}
yPos={popUp.yPos}
></ArtistPopUp>
})}
</div>
)
}
And the Child Component:
function ArtistGraph({addArtistPopUp}) {
// querying data
if(records) {
// wrangling data
const events = {
doubleClick: function(event) {
handleNodeClick(event)
}
}
return (
<div className='artist-graph'>
<Graph
graph={graph}
options={options}
events={events}
key={uniqueId()}
>
</Graph>
</div>
)
}
else{
return(<CircularProgress></CircularProgress>)
}
}
function areEqual(prevProps, nextProps) {
return true
}
export default React.memo(ArtistGraph, areEqual)
In any other case the rerendering of the Child component wouldn't be such a problem but sadly it causes the Graph to redraw.
So how do I manage to update the state of my parent Component without the Graph being redrawn?
Thanks in advance!
A few things, the child may be rerendering, but it's not for your stated reason. setState functions are guaranteed in their identity, they don't change just because of a rerender. That's why it's safe to exclude them from dependency arrays in useEffect, useMemo, and useCallback. If you want further evidence of this, you can check out this sandbox I set up: https://codesandbox.io/s/funny-carson-sip5x
In my example, you'll see that the parent components state is changed when you click the child's button, but that the console log that would fire if the child was rerendering is not logging.
Given the above, I'd back away from the usCallback approach you are using now. I'd say it's anti-pattern. As a word of warning though, your useCallback was missing a required dependency, artistPopUp.
From there it is hard to say what is causing your component to rerender because your examples are missing key information like where the graphs, options, or records values are coming from. One thing that could lead to unexpected rerenders is if you are causing full mounts and dismounts of the parent or child component at some point.
A last note, you definitely do not need to pass that second argument to React.memo.

React Context API Seems to re-render every component

I'm trying to use the new Context API in my app and it looks like every time I update the context, it re-renders any component connected to it regardless. I have a sandbox demo setup to see code and working issue. When you type in the input - the buttons context is rendered and vice-versa. My original thinking was that if you type in the input, only the input context would be printed out.
DEMO
Is this how it works or am I missing something?
Thanks,
Spencer
The way I avoid re-rendering with react context API:
First I write my component as pure functional component:
const MyComponent = React.memo(({
somePropFromContext,
otherPropFromContext,
someRegularPropNotFromContext
}) => {
... // regular component logic
return(
... // regular component return
)
});
Then I write a function to select props from context:
function select(){
const { someSelector, otherSelector } = useContext(MyContext);
return {
somePropFromContext: someSelector,
otherPropFromContext: otherSelector,
}
}
I have my connect HOC wrote:
function connect(WrappedComponent, select){
return function(props){
const selectors = select();
return <WrappedComponent {...selectors} {...props}/>
}
}
All together
import connect from 'path/to/connect'
const MyComponent ... //previous code
function select() ... //previous code
export default connect(MyComponent, select)
Usage
<MyComponent someRegularPropNotFromContext={something} />
Demo
Demo on codesandbox
Conclusion
MyComponent will re-render only if the specifics props from context updates with a new value, if the value is the same, it will not re-render. Also it avoid re-rendering on any other value from context that is not used inside MyComponent. The code inside select will execute every time the context updates, but as it does nothing, its ok, since no re-rendering of MyComponent is wasted.
That is the expected behaviour. Components as consumers re-renders when their provider data updates. Further more, shouldComponentUpdate hooks do not work on Consumers.
Quoting React's content API:
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
For more info check here

React PureComponent updates when props and state did not change

I'm currently playing around with ReactJS' PureComponent. I have a simple component which just shows some text inside nested PureComponents:
export class Test extends React.Component<ITestProps> {
componentDidMount(): void {
window.setInterval(() => this.forceUpdate(), 1500);
}
private readonly extraSmall = { size: 10 };
render(): JSX.Element {
console.log("render Login");
return (
<Bootstrap.Container fluid={true}>
<Bootstrap.Row>
<Bootstrap.Col xs={this.extraSmall}>
RENDERED!
</Bootstrap.Col>
</Bootstrap.Row>
</Bootstrap.Container>
);
}
}
I've exptected that the render would only be called once on each component. Container, Row and Col are all PureComponents.
However, they all got called once every 1.5 seconds and I don't get the point why.
What I have understood from the docs is, that even if the parent is updated during forceUpdate(), each child will call the shouldComponentUpdate which should return false for each child of Test or at least Container.
But in console is see render Login, render Container, render Row and render Col. But Container's props or state did not change. So why is there a re-render happening?
From the docs:
Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate().
This will trigger the normal lifecycle methods for child components, including the shouldComponentUpdate()
method of each child. React will still only update the DOM if the markup changes.
So even if this component does not make any real-life sense, it should not re-render at least Row and Col.
Maybe I am too late, but I will leave this answer for other users.
The reason is you are using forceUpdate which ignores shouldComponentUpdate.
The reason of forceUpdate existence is to make component re-render again and again ignoring props or state update, because sometimes it is needed.
Docs reference:
https://reactjs.org/docs/react-component.html#forceupdate

How can I make a wrapper component mount before its child components?

I'm trying to create a wrapper that does not mount the child if the user is not authenticated. Otherwise, it mounts and renders the child component.
Roughly looks like this:
export class RedirectOnCondition extends Component {
render(){
return this.props.isAuthenticated? this.props.children : null
}
}
My issue is the the child still mounts before the parent has a chance to evaluate the condition. It's only after the child's componentWillMount` (and any associated API calls have fired and failed) that the parent's render kicks in and remove's the child. According to this question this is how React works.
How can I get around this?
In the first render, the parent component may have not yet received the props you need to process the conditional rendering of a children component. In this case, you may want to check first if the props is already there.
export class RedirectOnCondition extends Component {
render(){
return "isAuthenticated" in this.props ? this.props.isAuthenticated? this.props.children : null : null
}
}

Categories