A-Sharabiani
A-Sharabiani

Reputation: 19329

Update the sibling component only

In the code below, how can I update only Component B and not Component A (or the Parent Component)

Component A:

const A = ({ data, clickCallback }) => {
  console.debug('A');

  return (<button onClick={clickCallback}>Component A</button>)
};

Component B:

const B = ({ filteredData }) => {
  console.debug('B');

  return <h1>Component B: {filteredData}</h1>;
};

Parent Component:

function Parent() {
    console.debug('parent');
    const [data, setData] = useState(0);
    const handleClick = () => {
      setData(data + 1);
    };

    return (
      <div>
        <A clickCallback={handleClick}/>
        <B filteredData={data} />
      </div>
    );
}

So when clicking on the Component A button, I only see console.debug('B') in the console?

parent
A
B
B
B
...

Here is the link to the working code: https://codesandbox.io/s/musing-lehmann-kme8d?file=/src/index.js:233-245

NOTE: I have tried wrapping the handleClick inside a useCallback() but, still in the console I see:

parent
A
B
parent
A
B
...

Upvotes: 0

Views: 535

Answers (1)

Drew Reese
Drew Reese

Reputation: 202751

With functional components, they are rerendered when their parent component rerenders. I.E. When Parent rerenders, then both A and B` will be rerendered in the "render phase" in order to compute a diff. This should not be confused with rendering to the DOM during the "commit phase".

You can wrap A in the memo Higher Order Component and pass a custom equality function that returns true/false if the previous and next props are equal.

memo

By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.

function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);

I suggest also correctly console logging in an useEffect so you truly know when the component is rerendered to the DOM.

A functional state update should also be used to update the data state so the state value isn't closed over in the callback scope.

const A = ({ data, clickCallback }) => {
  useEffect(() => {
    console.debug("A");
  });

  return <button onClick={clickCallback}>Component A</button>;
};

const MemoA = memo(
  A,
  (prevProps, nextProps) => prevProps.data === nextProps.data
);

const B = ({ filteredData }) => {
  useEffect(() => {
    console.debug("B");
  });

  return <h1>Component B: {filteredData}</h1>;
};

function Parent() {
  useEffect(() => {
    console.debug("parent");
  });
  const [data, setData] = useState(0);
  const handleClick = () => {
    setData(data => data + 1); // <-- functional update
  };

  return (
    <div>
      <MemoA clickCallback={handleClick} />
      <B filteredData={data} />
    </div>
  );
}

Edit update-the-sibling-component-only

Upvotes: 1

Related Questions