Why does my component rerender with React.memo

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

Answer

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.