skip to Main Content

I have App.js that looks like this:

import ChildComponent from "./ChildComponent";
import { useState, useMemo, useCallback } from "react";

export default function App() {
  const [pageTitle, setPageTitle] = useState("");
  const [anotherProp, setAnotherProp] = useState(1);

  const handleInputChange = useCallback(
    (e) => {
      setPageTitle(e.target.value);
    },
    [pageTitle]
  );

  const handleIncrement = useCallback(() => {
    setAnotherProp(anotherProp + 1);
  }, [anotherProp]);

  return (
    <div className="App">
      <ChildComponent title={pageTitle} handleInputChange={handleInputChange} />

      <div>
        {anotherProp}
        <input
          type={"button"}
          value={"Increment Count"}
          onClick={handleIncrement}
        />
      </div>
    </div>
  );
}

The child component looks like this:


function ChildComponent({ title, handleInputChange }) {
  console.log("Child Component renders");
  return (
    <>
      <input type="text" onChange={handleInputChange} />
      <h1>{title}</h1>
    </>
  );
}

export default ChildComponent;

Since I have used useCallBack I expect that when I click on the button to increment count, my child component should remain unaffected and not re-render. But my child component still re-renders.

Can you help me understanding why?

3

Answers


  1. That is the standard behaviour of React. It will rerender its children when the parent component rerenders, even if the child’s props didn’t change. In this case, your parent is rerendering as its state is updating (the anotherProp counter).

    If you want to prevent a particular component from rerendering when its props remain unchanged, you can use React.memo() by wrapping your ChildComponent in it, eg:

    export default memo(ChildComponent);
    

    With this, React won’t rerender your ChildComponent when its props remain unchanged.

    On a side note, the [pageTitle] dependency for your first useCallback() hook is unnecessary, and can instead just be [] as your callback does not use this state value inside of it.

    Login or Signup to reply.
  2. Another version of answer. Combination of useCallback and useMemo

    App.js

    import React, { useState, useCallback, useMemo } from 'react';
    import { render } from 'react-dom';
    import Child from './components/child.js';
    
    function App() {
      const [num, setNum] = useState(0);
    
      const handleUpdateNum = useCallback((num) => {
        setNum(num + 1);
      }, []);
    
      const getChildComp = useMemo(
        () => <Child handleUpdateNum={handleUpdateNum} />,
        [handleUpdateNum]
      );
    
      return (
        <div>
          <h1>{num}</h1>
          {getChildComp}
          <button onClick={() => handleUpdateNum(num)}> Addition </button>
        </div>
      );
    }
    

    Child.js

    import React from 'react';
    function Child() {
      console.log('child component rendered');
    
      return (
        <div>
          <h1>Hello World</h1>
        </div>
      );
    }
    export default Child;
    

    Hope, this helps.

    Login or Signup to reply.
  3. The issue is with the dependency array in the useCallback for handleInputChange. You have pageTitle as a dependency, which causes the handleInputChange function to be recreated whenever pageTitle changes.
    So Remove pageTitle from the dependency array to prevent unnecessary recreations, and it should work as expected.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search