Home > other >  How to avoid rerendering all list items
How to avoid rerendering all list items

Time:09-10

On every click react rerender every item. How to avoid it? I want react to only render items that have changed. I tried using react memo and usecallback but it didn't help. I can't understand what is the reason. What are the ways to solve this problem? Thank you.

App.ts

import React, { useState } from "react";
import "./styles.css";
import ListItem from "./ListItem";

const items = [
  { id: 1, text: "items1" },
  { id: 2, text: "items2" },
  { id: 3, text: "items3" },
  { id: 4, text: "items4" },
  { id: 5, text: "items5" },
  { id: 6, text: "items6" },
  { id: 7, text: "items7" }
];

export default function App() {
  const [activeIndex, setActiveIndex] = useState(1);
  const onClick = (newActiveIndex: number) => {
    setActiveIndex(newActiveIndex);
  };

  return (
    <div className="App">
      {items.map(({ id, text }) => (
        <ListItem
          key={id}
          id={id}
          text={text}
          activeId={activeIndex}
          onClick={onClick}
        />
      ))}
    </div>
  );
}

Item.ts

import React from "react";

interface ListItemProps {
  id: number;
  activeId: number;
  text: string;
  onClick: (newActiveIndex: number) => void;
}

const ListItem: React.FC<ListItemProps> = ({ id, activeId, text, onClick }) => {
  const isActive = activeId === id;
  const style = {
    marginRight: "42px",
    marginTop: "24px",
    color: isActive ? "green" : "red"
  };
  console.log(`update id: ${id}`);

  return (
    <div>
      <span style={style}>id: {id}</span>
      <span style={style}>is active: {isActive ? "active" : "inactive"}</span>
      <span style={style}>text: {text}</span>
      <button onClick={() => onClick(id)}>setActive</button>
    </div>
  );
};

export default ListItem;

CodePudding user response:

activeIndex changes with each click, so all of the components have new prop values and will need to re-render.

Instead of passing the activeIndex to every component every time:

const ListItem: React.FC<ListItemProps> = ({ id, activeId, text, onClick }) => {
  const isActive = activeId === id;

Pass an isActive to each component:

const ListItem: React.FC<ListItemProps> = ({ id, isActive, text, onClick }) => {

Effectively moving the calculation of the bool from the component to the consuming code:

<ListItem
  key={id}
  id={id}
  text={text}
  isActive={activeIndex === id}
  onClick={onClick}
/>

Then memoization (coupled with useCallback for the onClick handler) should work because most components (the ones which aren't changing their "active" state) won't receive new props and won't need to re-render.

CodePudding user response:

Even with memoization it won't work, because your onClick function changes your components state, and your children components depend on activeIndex value, so every time you click you change the components state, and when state changes, the component re-renders itself and it's children, if your children didn't depend on your state, they wouldn't re-render (if you use memoization).

CodePudding user response:

Plus to David's answer i would return ListItem in React.memo React.memo(ListItem)

  • Related