Why does React re-render children when the parent changes? - reactjs

If a parent re-renders, children in React are also re-rendered, no matter if the passed props changed or not.
Why is React doing that? What would be the issue if React wouldn't re-render children (without changed props) when the parent renders?
Update: I am talking about this in the React Devtools profiler:
Sample code:
App.tsx:
import React, { useMemo, useState } from "react";
import "./App.css";
import { Item, MyList } from "./MyList";
function App() {
console.log("render App (=render parent)");
const [val, setVal] = useState(true);
const initialList = useMemo(() => [{ id: 1, text: "hello world" }], []); // leads to "The parent component rendered"
//const initialList: Item[] = []; // leads to "Props changed: (initialList)"
return (
<div style={{ border: "10px solid red" }}>
<button type="button" onClick={() => setVal(!val)}>
re-render parent
</button>
<MyList initialList={initialList} />
</div>
);
}
export default App;
MyList.tsx:
import { FC, useState } from "react";
export interface Item {
id: number;
text: string;
}
interface Props {
initialList: Item[];
//onItemsChanged: (newItems: Item[]) => void;
}
export const MyList: FC<Props> = ({
initialList,
//onItemsChanged,
}) => {
console.log("render MyList");
const [items, setItems] = useState(initialList);
return (
<div style={{ border: "5px solid blue" }}>
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
<button type="button">add list item (to be implemented)</button>
</div>
);
};

React achieves a fast and responsive UI by re-rendering components on every state change (using setState) or from changes of props, followed by React’s reconciliation diffing algorithm that diffs previous renders with current render output to determine if React should commit changes to the component tree (e.g. DOM) with the new updates.
However, unnecessary component re-renders will happen and can be expensive, It’s been a common performance pitfall in every single React project that I’ve been working on. SOURCE
Solution for this issue :
A component can re-render even if its props don’t change. More often than not this is due to a parent component re-rendering causing the child to re-render.
To avoid this, we can wrap the child component in React.memo() to ensure it only re-renders if props have changed:
function SubComponent({ text }) {
return (
<div>
SubComponent: { text }
</div>
);
}
const MemoizedSubComponent = React.memo(SubComponent);
SOURCE

Memoization generates an additional cost corresponding to cache-related computations, this is why React re-renders components even when the props are referentially the same, unless you choose to memoize things using React.memo for instance.
If you consider for example a component that re-renders with different props very often, and if memoization was an internal implementation detail, then React would have to do 2 jobs on every re-rendering:
Check if the old and current props are referentially the same.
Because props comparison almost always returns false, React would then perform the diff of previous and current render results.
which means that you might end up with worse performance.

Wrapp your component with React.memo and it will not re-render
export const MyList: FC<Props> = React.memo(({
initialList,
//onItemsChanged,
}) => {
console.log("render MyList");
const [items, setItems] = useState(initialList);
return (
<div style={{ border: "5px solid blue" }}>
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
<button type="button">add list item (to be implemented)</button>
</div>
);
})
If you are looking at reason, please see this

Well, the component only re-renders if shouldComponentUpdate() returns true, which it does by default. It is usually not much of a problem because the DOM will still not update if there are no changes. As an optimization, you can still add logic to keep your child components from re-rendering if its state and props have not changed, as follows:
shouldComponentUpdate(nextProps, nextState) {
return (this.props.someProp !== nextProps.someProp && this.state.someState !== nextState.someState);
}
Do remember though this is just a performance optimization, when in doubt stick to the default behaviour. Here is a link to the official documentation.

Here is a little analogy that should help
Let's say you have a box, and a box within that box. If you want to replace the outer box with a new box, then you must also replace the box inside it.
Your parent component is like the outer box, and the child component like the inner box. If you clear away the first render to make room for a new render, that new render will re-render new child components inside it.
I hope this helps clear things up for you.
Edit:
If you were not to re-render the child component inside the parent then any props that might be passed to the child component would not update within it and therefore, the child would be forever without dynamic props, and the whole purpose of ReactJS would be lost. You wouldn't be here to ask this question in the first place anyway if that was the case.

When Parent Component render will not render child components,for using memo and useCallback.
Child Component:
Using memo will cause React to skip rendering a component if its props have not changed.
import { React, memo } from "react";
import { Typography, TextField } from "#mui/material";
function PasswordComponent(props) {
console.log("PasswordComponenmt");
return (
<div>
<Typography style={{ fontWeight: "900", fontSize: 16 }}>
Password
</Typography>
<TextField
size="small"
type="password"
variant="filled"
value={props.password}
onChange={(e) => {
props.onChangePassword(e.target.value);
}}
/>
</div>
);
}
export default memo(PasswordComponent);
Parent Component:
The React useCallback Hook returns a memoized callback function.
The useCallback Hook only runs when one of its dependencies update.
This can improve performance.
import { React, useState, useCallback } from "react";
export default function ParentComponent() {
const [password, setpassword] = useState("");
const onChangePassword = useCallback((value) => {
setpassword(value);
}, []);
return <PasswordComponent onChangePassword={onChangePassword} />;
}

Related

Why is React rendering the children in this scenario? (State is the same, using useState)

Code Sandbox
import React, { useState, useEffect } from "react";
const Foo = () => {
console.log("render foo");
return <div> foo</div>;
};
const App = () => {
const [value, setValue] = useState(1);
useEffect(() => {
console.log("effect", value);
}, [value]);
console.log("rendering");
return (
<div>
{" "}
<Foo /> <button onClick={() => setValue(value)}>Click To Render</button>
</div>
);
};
export default App;
Now according to the React Documentation
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (emphasis mine)
(React uses the Object.is comparison algorithm.)
Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with useMemo.
In the example I've given, we can see that the useEffect hook doesn't fire, as described by the documentation, but my Foo component is rendering.
Why is this?
I thought that maybe the inline function causes a render - but if I change that to a memoized function using useCallback the same behaviour happens:
const handleClick = useCallback(() => setValue(value), [value]);
console.log("rendering");
return (
<div>
{" "}
<Foo /> <button onClick={handleClick}>Click To Render</button>
</div>
The bail out logic was implemented in v16.8.0 of react-dom in which react also introduced hooks, whereas your demo uses the alpha version of hooks which is why you still see a re-render being triggered even when you update with the same state
According to v16.8.0 release notes
Bail out of rendering on identical values for useState and useReducer Hooks. (#acdlite in #14569)

ReactJs: Prevent Rerender of wrapped component

I'm trying to prevent a re-render when using custom hook for hours now -.-, need some help ;O|
(Dont know if I should call this custom hook or functional hoc though)
I have a MessageList component that display a SimpleMessage wrapped in WithAvatarHeader.
Here is my profiler result:
Every time I add a message to the list, all messages are rendered again.
This isn't happening when I only use SimpleMessage in MessageList
Is there a way to memo(WithAvatarHeader) ?
MessageList :
import React from "react";
import SimpleMessage from "./SimpleMessage";
import WithAvatarHeader from "./WithAvatarHeader";
const MessageList = props => {
const Message = WithAvatarHeader(SimpleMessage);
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
SimpleMessage:
import React, { memo } from "react";
const SimpleMessage = props => {
return (
<div className="simple-message">
{props.message}
</div>
);
};
export default memo(SimpleMessage);
WithAvatarHeader:
import React from "react";
const WithAvatarHeader = WrappedComponent => props => {
return <WrappedComponent {...props} />;
};
export default WithAvatarHeader;
Thanks for the help :-)
You should not declare component inside another component.
Once you move declaration outside:
const Message = WithAvatarHeader(SimpleMessage);
const MessageList = props => {
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
you will be fine.
Reason is reconciliation process that decides what's to drop, what to create and what to update.
Besides your JSX says it still same element <Message> React checks component's constructor(it does not work with text representation from JSX). And it will referentially different(since you re-declare this constructor on next render). So React drops every <Message> and create them from scratch. Keeping declaration outside your MessageList means constructor is referentially the same so React will not re-create <Message> till key is the same.

(React) How to put focus on first element when component updates?

I have the following code in react.
In ParentComponent, I render either first or second panel depending on whether certain conditions are met. Let's say FirstPanel is rendered.
Inside FirstPanel, I am able to do some interactions that would update the panel and I need the focus to go to the first element in the first panel (aka the first div element)
How can I do this? I tried using this.props.setRef.focus() but it doesn't work.
ParentComponent
setCurrentPanel(panel) {
this.currentPanel = panel;
}
render() {
if (this.state.showFirstPanel)
return <FirstPanel setRef={this.setCurrentPanel} />;
if (this.state.showSecondPanel)
return <SecondPanel setRef={this.setCurrentPanel} />;
return undefined;
}
FirstPanel
componentDidUpdate() {
// put focus on component FirstPanel again
}
render() {
return <div role="tabpanel" tabIndex="0" ref={this.props.setRef}>
{children}
</div>
}
Using useRef and useEffect you can do that way:
import React , {useRef, useEffect, useState} from 'react';
const MyComponent = () => {
const [valueThatWillChange, setValueWillChange] = useState(null);
const ref = useRef(null);
useEffect(() => {
ref.current.focus();
}, [valueThatWillChange]);
return (
<div>
<input type="text" ref={ref} />
<input type="text" onBlur={e => setValueThatWillChange(e.target.value)} />
</div>
);
};
Each time that the cursor out of second input he will back to the first one
Based on your ParentComponent implementation, when the state changes, FirstPanel will unmount, and then SecondPanel mounts.
When you switch again, SecondPanel unmounts, and FirstPanel mounts.
To achieve what you are after, simply do the focus manipulation in both componentDidUpdate and componentDidMount lifecycles.
In addition, if your ParentComponent is not going to anything with currentPanel, you can creatRef in each Panel component. Your setRef at the moment is simply an function, not a Ref object that contains reference to the dom element.

When to use useImperativeHandle, useLayoutEffect, and useDebugValue

I cannot understand why the following useImperativeHandle, useLayoutEffect, and useDebugValue hooks are needed, can you give examples when they can be used, but not examples from the documentation please.
Allow me to preface this answer by stating that all of these hooks are very rarely used. 99% of the time, you won't need these. They are only meant to cover some rare corner-case scenarios.
useImperativeHandle
Usually when you use useRef you are given the instance value of the component the ref is attached to. This allows you to interact with the DOM element directly.
useImperativeHandle is very similar, but it lets you do two things:
It gives you control over the value that is returned. Instead of returning the instance element, you explicitly state what the return value will be (see snippet below).
It allows you to replace native functions (such as blur, focus, etc) with functions of your own, thus allowing side-effects to the normal behavior, or a different behavior altogether. Though, you can call the function whatever you like.
There could be many reasons you want might to do either of the above; you might not want to expose native properties to the parent or maybe you want to change the behavior of a native function. There could be many reasons. However, useImperativeHandle is rarely used.
useImperativeHandle customizes the instance value that is exposed to parent components when using ref
Example
In this example, the value we'll get from the ref will only contain the function blur which we declared in our useImperativeHandle. It will not contain any other properties (I am logging the value to demonstrate this). The function itself is also "customized" to behave differently than what you'd normally expect. Here, it sets document.title and blurs the input when blur is invoked.
const MyInput = React.forwardRef((props, ref) => {
const [val, setVal] = React.useState('');
const inputRef = React.useRef();
React.useImperativeHandle(ref, () => ({
blur: () => {
document.title = val;
inputRef.current.blur();
}
}));
return (
<input
ref={inputRef}
val={val}
onChange={e => setVal(e.target.value)}
{...props}
/>
);
});
const App = () => {
const ref = React.useRef(null);
const onBlur = () => {
console.log(ref.current); // Only contains one property!
ref.current.blur();
};
return <MyInput ref={ref} onBlur={onBlur} />;
};
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
useLayoutEffect
While similar to some extent to useEffect(), it differs in that it will run after React has committed updates to the DOM. Used in rare cases when you need to calculate the distance between elements after an update or do other post-update calculations / side-effects.
The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Example
Suppose you have an absolutely positioned element whose height might vary and you want to position another div beneath it. You could use getBoundingClientRect() to calculate the parent's height and top properties and then just apply those to the top property of the child.
Here you would want to use useLayoutEffect rather than useEffect. See why in the examples below:
With useEffect: (notice the jumpy behavior)
const Message = ({boxRef, children}) => {
const msgRef = React.useRef(null);
React.useEffect(() => {
const rect = boxRef.current.getBoundingClientRect();
msgRef.current.style.top = `${rect.height + rect.top}px`;
}, []);
return <span ref={msgRef} className="msg">{children}</span>;
};
const App = () => {
const [show, setShow] = React.useState(false);
const boxRef = React.useRef(null);
return (
<div>
<div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
{show && <Message boxRef={boxRef}>Foo bar baz</Message>}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
.box {
position: absolute;
width: 100px;
height: 100px;
background: green;
color: white;
}
.msg {
position: relative;
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
With useLayoutEffect:
const Message = ({boxRef, children}) => {
const msgRef = React.useRef(null);
React.useLayoutEffect(() => {
const rect = boxRef.current.getBoundingClientRect();
msgRef.current.style.top = `${rect.height + rect.top}px`;
}, []);
return <span ref={msgRef} className="msg">{children}</span>;
};
const App = () => {
const [show, setShow] = React.useState(false);
const boxRef = React.useRef(null);
return (
<div>
<div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
{show && <Message boxRef={boxRef}>Foo bar baz</Message>}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
.box {
position: absolute;
width: 100px;
height: 100px;
background: green;
color: white;
}
.msg {
position: relative;
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
useDebugValue
Sometimes you might want to debug certain values or properties, but doing so might require expensive operations which might impact performance.
useDebugValue is only called when the React DevTools are open and the related hook is inspected, preventing any impact on performance.
useDebugValue can be used to display a label for custom hooks in React DevTools.
I have personally never used this hook though. Maybe someone in the comments can give some insight with a good example.
useImperativeHandle
useImperativeHandle allows you to determine which properties will be exposed on a ref. In the example below, we have a button component, and we'd like to expose the someExposedProperty property on that ref:
[index.tsx]
import React, { useRef } from "react";
import { render } from "react-dom";
import Button from "./Button";
import "./styles.css";
function App() {
const buttonRef = useRef(null);
const handleClick = () => {
console.log(Object.keys(buttonRef.current)); // ['someExposedProperty']
console.log("click in index.tsx");
buttonRef.current.someExposedProperty();
};
return (
<div>
<Button onClick={handleClick} ref={buttonRef} />
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
[Button.tsx]
import React, { useRef, useImperativeHandle, forwardRef } from "react";
function Button(props, ref) {
const buttonRef = useRef();
useImperativeHandle(ref, () => ({
someExposedProperty: () => {
console.log(`we're inside the exposed property function!`);
}
}));
return (
<button ref={buttonRef} {...props}>
Button
</button>
);
}
export default forwardRef(Button);
Available here.
useLayoutEffect
This is the same as useEffect, but only fires once all DOM mutations are completed. This article From Kent C. Dodds explains the difference as well as anyone, regarding these two, he says:
99% of the time [useEffect] is what you want to use.
I haven't seen any examples which illustrate this particularly well, and I'm not sure I'd be able to create anything either. It's probably best to say that you ought to only use useLayoutEffect when useEffect has issues.
useDebugValue
I feel like the docs do a pretty good example of explaining this one. If you have a custom hook, and you'd like to label it within React DevTools, then this is what you use.
If you have any specific issues with this then it'd probably be best to either comment or ask another question, because I feel like anything people put here will just be reiterating the docs, at least until we reach a more specific problem.
The useImperativeHandle hook helped me a lot with a use case of mine.
I created a grid component which uses a third-party library component. The library itself has a big data layer with built-in functionality that can be used by accessing the instance of the element.
However, in my own grid component, I want to extend it with methods which perform actions on the grid. Furthermore, I also want to be able to execute those methods from outside of my grid component.
This is easily achievable by adding the methods inside of the useImperativeHandle hook, and then they will be exposed and usable by its parent.
My grid component looks kind of like this:
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import ThirdPartyGrid from 'some-library';
export default forwardRef((props, forwardedRef) => {
const gridRef = useRef(null);
useImperativeHandle(forwardedRef,
() => ({
storeExpandedRecords () {
// Some code
},
restoreExpandedRecords () {
// Some code
},
}));
return (
<div ref={forwardedRef}>
<ThirdPartyGrid
ref={gridRef}
{...props.config}
/>
</div>
);
});
And then in my parent, I can execute those methods:
import React, { useRef, useEffect } from 'react';
export default function Parent () {
const gridRef = useRef(null);
const storeRecords = () => {
gridRef.current.storeExpandedRecords();
};
useEffect(() => {
storeRecords();
}, []);
return <GridWrapper ref={gridRef} config={{ something: true }} />
};
useImperativeHandle
usually hook expose your functional based component method and properties to other component by putting functional component inside forwardRef
example
const Sidebar=forwardRef((props,ref)=>{
const [visibility,setVisibility]=useState(null)
const opensideBar=()=>{
setVisibility(!visibility)
}
useImperativeHandle(ref,()=>({
opensideBar:()=>{
set()
}
}))
return(
<Fragment>
<button onClick={opensideBar}>SHOW_SIDEBAR</button>
{visibility==true?(
<aside className="sidebar">
<ul className="list-group ">
<li className=" list-group-item">HOME</li>
<li className=" list-group-item">ABOUT</li>
<li className=" list-group-item">SERVICES</li>
<li className=" list-group-item">CONTACT</li>
<li className=" list-group-item">GALLERY</li>
</ul>
</aside>
):null}
</Fragment>
)
}
//using sidebar component
class Main extends Component{
myRef=createRef();
render(){
return(
<Fragment>
<button onClick={()=>{
///hear we calling sidebar component
this.myRef?.current?.opensideBar()
}}>
Show Sidebar
</button>
<Sidebar ref={this.myRef}/>
</Fragment>
)
}
}
I see that there are already answers to this question. So, I just want to share my articles about useImperativeHandle, useEffect, and useLayoutEffect.
I have written an example-based article aboutuseImperativeHandle hook, you can check it out here: useImperativeHandle by Examples
I also wrote an article about effects in React which explains useEffect hook and useLayoutEffect hook differences in detail: A Beginner’s Guide to Effects in React

Hooks equivalent for componentWilLReceiveProps to update state

So I have a component where I conditionally update state on props change. If current state's CurrentPage is not equal to next props CurrentPage, I update state with next props' CurrentPage:
public componentWillReceiveProps(nextProps) {
if (this.state.CurrentPage !== nextProps.CurrentPage) {
this.setState({ CurrentPage: nextProps.CurrentPage });
}
}
I'm in the middle of refactoring the component do use hooks. I have a useState hook setup for CurrentPage when the component first loads:
const [currentPage, setCurrentPage] = useState(props.CurrentPage ? props.CurrentPage : 1);
What would be the hook equivalent of the componentWillReceiveProps logic in this case? Thanks!
use the "useEffect" hook for such purposes.
useEffect(() => {
if(props.yourproperty){
//execute your code.
}
console.log('property changed', props.yourproperty);
},[props.yourproperty])
This will only be called in case props.yourproperty is changed so no need to check with old props.
UseEffect Hook docs
You'll utilize the useEffect() hook. For an explanation of why, please read my guide to understand the hook and how you can leverage it: React Hooks Guide - useEffect (give it some time to load, codesandbox is a bit slow).
Your example is a bit arbitrary, as your props will already contain the current page, but nevertheless, here's a working example...
Working example (props updating another component's state):
components/Navigation
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import { Link, withRouter } from "react-router-dom";
const style = {
marginRight: 5
};
const Navigation = ({ location: { pathname }, children }) => {
// initializing "currentPage" with the current "pathname" prop
// initializing "setPage" function to update "currentPage"
const [currentPage, setPage] = useState(pathname);
// utilizing useEffect to keep track of "pathname" changes
// that, upon change, will update the "currentPage" state
useEffect(() => {
setPage(pathname);
}, [pathname]);
return (
<div className="container">
<div className="nav">
<Link style={style} to="/">
Home
</Link>
<Link style={style} to="/page1">
Page 1
</Link>
<Link style={style} to="/page2">
Page 2
</Link>
</div>
<div className="page">
<p>
(current page: <strong>{currentPage}</strong> )
</p>
</div>
{children}
</div>
);
};
Navigation.propTypes = {
pathname: PropTypes.string
};
export default withRouter(Navigation);

Resources