3

Every time my tabs component parent rerenders, my child component rerenders as well.

My tabs component parent is the following

import { useState } from "react";
import "./styles.scss";
import Tab from "./tab";

export default function App() {
  const [selectTab, setSelectTab] = useState("a");
  console.log("parent render");
  return (
    <div className="App">
      <div className="tab-list">
        <Tab tab={"a"} title={"First Title"} setSelectTab={setSelectTab} />
        <Tab tab={"b"} title={"Second Title"} setSelectTab={setSelectTab} />
        <Tab tab={"c"} title={"Third Title"} setSelectTab={setSelectTab}
        />
      </div>
      {selectTab === "a" && <div>this is a</div>}
      {selectTab === "b" && <div>this is b</div>}
      {selectTab === "c" && <div>this is c</div>}
    </div>
  );
}

My tab component code

const Tab = ({ title, tab, setSelectTab }) => {
  console.log("child render");
  const handleClick = (tab) => {
    setSelectTab(tab);
  }
  return <p onClick={() => handleClick(tab)}>{title}</p>;
};

export default Tab;

Every render I console log "parent render" once and "child render" three times.

The component props does not change whenever the parent state changes, so I thought I could skip the component rerendering with React.memo and made the following changes:

My component parent

import { useState } from "react";
import "./styles.scss";
import MemoizedTab from "./tab";

export default function App() {
  const [selectTab, setSelectTab] = useState("a");
  console.log("parent render");
  return (
    <div className="App">
      <div className="tab-list">
        <MemoizedTab
          tab={"a"}
          title={"First Title"}
          setSelectTab={setSelectTab}
        />
        <MemoizedTab
          tab={"b"}
          title={"Second Title"}
          setSelectTab={setSelectTab}
        />
        <MemoizedTab
          tab={"c"}
          title={"Third Title"}
          setSelectTab={setSelectTab}
        />
      </div>
      {selectTab === "a" && <div>this is a</div>}
      {selectTab === "b" && <div>this is b</div>}
      {selectTab === "c" && <div>this is c</div>}
    </div>
  );
}

My memoized tab component code

import React, { useCallback } from "react";

const Tab = ({ title, tab, setSelectTab }) => {
  console.log("child render");
  const handleClick = useCallback(
    (tab) => {
      setSelectTab(tab);
    },
    [setSelectTab]
  );
  return <p onClick={() => handleClick(tab)}>{title}</p>;
};

export default Tab;
export const MemoizedTab = React.memo(Tab);

But I console log the same way as my non-memoized code. What is causing the tab component rerendering and how can I stop the unnecessary rerendering?

I suspect it could be the setSelectTab function from the parent that is a new function on every rerender which causes the component to rerender despite the useCallback.

Codesandbox link

1
  • I noticed that useCallback is unnecessary here since the component is not receiving a callback function from the parent. Commented Aug 23, 2021 at 6:05

1 Answer 1

4

The following import statement

import MemoizedTab from "./tab";

imports the default export - not the named export.

Memoized Tab component is exported as a named export and the one exported as a default export is NOT memoized.

This is why there is unnecessary re-rendering of the Tab component.

Change the import statement to

import { MemoizedTab } from "./tab";

to prevent unnecessary re-rendering of the Tab component.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.