0

I have been trying to avoid re-rendering the child components (InputBox component) if the props are not changing but below code is not working as expected. How can I fix it ? How to avoid re-rendering InputBox component this as name is same but memoizedCallbacks is not memoized

<InputBox name={"test"} handleCb={memoizedCallback} id="name" /> 

<body> <div id="root"></div> <script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <script src="https://unpkg.com/@babel/[email protected]/babel.js"></script> <script type="text/babel"> // child var InputBoxSet = new Set(); const InputBox = React.memo(function InputBox({ name, handleCb }) { // should not re-render on name or handlerCB change console.log('%c InputBox renders', 'color: orange; font-weight: bold;'); InputBoxSet.add(name); InputBoxSet.add(handleCb); console.log('%c InputBoxSet', 'color: orange; font-weight: bold;',InputBoxSet); return <input value={name} onChange={handleCb} id="name" />; }); // parent let mySet = new Set(); // to check if the callback function is new each it re-renders const Greeting = React.memo(function Greeting(props) { console.log('%c greeting renders!', 'color: green; font-weight: bold;'); const [name, setName] = React.useState(""); const handleChange = (event) => { console.log("handleChange"); setName(event.target.value); }; // despite memoizing callback , the memoize callback function is // getting created new each time and hence causing the re-rendering of child const memoizedCallback = React.useCallback( (event) => { console.log('%c event', 'color: red; font-weight: bold;'); handleChange(event); // doesn't call originial function unless event is changed }, [event] ); mySet.add(memoizedCallback); console.log('%c mySet', 'color: green; font-weight: bold;',mySet); return ( <div> <form> <label htmlFor="name">Name: </label> <input value={name} onChange={memoizedCallback} id="name" /> {/* how to avoid re-rendering this */} <InputBox name={"test"} handleCb={memoizedCallback} id="name" /> </form> {name ? <strong>Hello {name}</strong> : "Please type your name"} </div> ); }, areEqual); function areEqual(prevProps, nextProps) { return prevProps.name === nextProps.name; } function App(props) { console.log('%c render App', 'color: Yellow; font-weight: bold;'); const [count, setCount] = React.useState(0); const nameArr = []; return ( <> <button onClick={() => setCount((c) => c + 1)}>{count}</button> <br /> <br /> <Greeting name={nameArr} /> </> ); } ReactDOM.render(<App />, document.getElementById("root")); </script> </body>

5
  • 1
    memo is used when the state changes in the parent component but that child component doesn't depend on it. In your case, it seems that the passed memoizedCallback dependency changes every time the internal state in Greeting (when you type in the input field) changes, and thus the props to InputBox changes every time you type. That's why it doesn't work. Commented Dec 26, 2020 at 13:45
  • Thank you but I know the reason for this .I event commented the reason for re-rending but how can i fix it.How can i avoid this Commented Dec 26, 2020 at 14:12
  • You cannot achieve this as long as you are passing the memoizedCallback that changes on every input change. Also, why do you want to avoid it? Re-rendering in you case is not that important/expensive. Commented Dec 26, 2020 at 15:45
  • I was just curious. I actually wanted to understand how useCallback works. I found out that it just avoids calling the actual function but it does create new wrapper function every time on rendering. If I have a child tree that takes this memoized callback as prop, it might be expensive to re-render this child every time, then how can i fix this re-rendering issue. Commented Dec 26, 2020 at 15:58
  • The only way to get memo and useCallback to not re-render/call the child component/function is to not update the props/dependency array passed to memo/useCallback. Once you understand this, implementing it is supposed to be straightforward. Commented Dec 26, 2020 at 17:18

1 Answer 1

1

InputBox Component receives 2 props name (primitive type) and handleCb (reference type) and the equality comparison used is

function areEqual(prevProps, nextProps) { return prevProps === nextProps; } 

prevProps will be an Object, for ex -

prevProps = { name: 'someName', handleCb: someFunc } 

nextProps will be an Object, for ex -

nextProps = { name: 'someOtherName', handleCb: someFunc } 

Since both are different references, they will never be equal, as {} === {} is always false. Hence, areEqual will always return false and the component will re render.

What you need is -

function areEqual(prevProps, nextProps) { return prevProps.name === nextProps.name; } 
Sign up to request clarification or add additional context in comments.

2 Comments

I know I can fix by using function areEqual(prevProps, nextProps) { return prevProps.name === nextProps.name; } but I want to avoid rendering even when i update only in Greeting
how to avoid re-rendering this <InputBox name={"test"} handleCb={memoizedCallback} id="name" /> as name is not changing but memoizedcallback is getting created each time

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.