SuganthiRaj
SuganthiRaj

Reputation: 103

How to render only subchild without rendering all high level component

I m having one sub child component which is inside a loop of parent component. when one of the sub child components is updating the state of parent component, it is re-rendering the all children since it is loop. How can i avoid the re-render for each iteration. It should update that particular sub child.

import React, { useState } from "react";

function Parent() {
  const [selectedChild, setSelectedChild] = useState([]);

  const onChangeHandle = (event, id) => {
    const checked = event.target.checked;
    let updatedArray = [...selectedChild];
    if (checked) {
      if (!selectedChild.includes(id)) {
        updatedArray.push(id);
      }
    } else {
      var index = updatedArray.indexOf(id);
      if (index !== -1) {
        updatedArray.splice(index, 1);
      }
    }
    setSelectedChild(updatedArray);
  };

  return (
    <div>
      <table>
        <tbody>
          {[1, 2, 3].map((value, index) => {
            return (
              <Child
                key={index}
                index={index}
                value={value}
                handle={onChangeHandle}
                isSelected={selectedChild.includes(index)}
              />
            );
          })}
        </tbody>
      </table>
      <div>{selectedChild}</div>
    </div>
  );
}

function Child({ index, value, handle, isSelected }) {
  console.log("rendering child");

  return (
    <tr>
      <td>
        <SubChild
          isChecked={isSelected}
          onChangeHandle={handle}
          index={index}
        />
      </td>
      <td>
        hello {index} {value}
      </td>
    </tr>
  );
}

function SubChild({ isChecked, onChangeHandle, index }) {
  console.log("rendering subchild");

  return (
    <input
      type="checkbox"
      checked={isChecked}
      onChange={(event) => onChangeHandle(event, index)}
    />
  );
}

export default function App() {
  return (
    <div className="App">
      <Parent />
    </div>
  );
}

Current behaviour: In above code, When i m clicking on the checkbox(which is Sub child) in one of the children component, it is updating the parent component state(selectedChild). So the loop is executing and all children(all table rows) are re rendering.

Expected behaviour: Only that particular sub child have to go for re-render (even it should not re-render child)

Demo: https://codesandbox.io/s/reactqa2-0c0md?file=/src/App.js

Little related question: How to avoid rerender all child components which in loop when parent component state update

Upvotes: 1

Views: 170

Answers (1)

Dennis Vash
Dennis Vash

Reputation: 53884

You should use memoization (useCallback/React.memo) and rewrite handle logic with functional updates.

Also, you avoid Child to render, since you have a new value after rendering.

// Make a stable callback
const onChangeHandle = useCallback((event, id) => {
  setSelectedChild((updatedArray) => {
    if (event.target.checked) {
      if (!updatedArray.includes(id)) {
        return [...updatedArray, id];
      }
    } else {
      return updatedArray.filter((currId) => currId !== id);
    }
    return updatedArray;
  });
}, []);

// Memoize the component
const MemoChild = React.memo(Child);
const MemoSubChild = React.memo(SubChild);

Edit reactQA2 (forked)

Upvotes: 1

Related Questions