Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 943
dev_el Avatar asked Dec 09 '25 12:12

dev_el


1 Answers

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.

like image 131
Yousaf Avatar answered Dec 11 '25 02:12

Yousaf



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!