且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何避免在父组件状态更新时重新渲染循环中的所有子组件

更新时间:2023-12-02 19:54:10

为此你可以使用 React.memo 如果 props 保持不变,它会记住你的组件.但是鉴于您的代码,您需要进行一些额外的更改:

  • 你必须应用useCallback来记住onChangeHandle函数;

  • 要正确记住 onChangeHandle,您需要重构它.你不能直接传递 selectedChild ,否则它会记住它的值.使用 setSelectedChild 作为参数传递一个接受 selectedChild 的函数.

  • 你的孩子应该接收 isSelected 作为布尔值而不是函数.否则 props 将保持不变并且 Child 永远不会更新;

    import React, { useState, memo, useCallback } from "react";函数父(){const [selectedChild, setSelectedChild] = useState([]);const onChangeHandle = useCallback((event, id) => {setSelectedChild(selectedChild => {const 检查 = event.target.checked;让 updatedArray = [...selectedChild];如果(检查){如果 (!selectedChild.includes(id)) {updatedArray.push(id);}} 别的 {var index = updatedArray.indexOf(id);如果(索引!== -1){updatedArray.splice(index, 1);}}返回更新数组;});}, []);const 虚拟 = id =>{返回 selectedChild.includes(id);};const renderChildren = () =>[1, 2, 3].map((value, index) => {返回 (<孩子键={索引}索引={索引}价值={价值}句柄={onChangeHandle}isSelected={dummy(index)}/>);});返回 (<div><表格><tbody>{renderChildren()}</tbody><div>{selectedChild}</div>

    );}const Child = memo(({ index, value, handle, isSelected }) => {console.log(渲染");返回 (<tr><td>句柄(事件,索引)}/></td><td>你好 {index} {value}</td></tr>);});导出默认函数 App() {返回 (

    <父/>

    );}

https://stackblitz.com/edit/so-memo-children?file=src/App.js

I m having one child component which is inside a loop of parent component. when one of the 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.


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);
  }
  const dummy = (id) => {
    return selectedChild.includes(id);
  }
  return (
    <div>
    <table>
    <tbody>
      {[1,2,3].map((value, index) => {
      return (
        <Child 
        key={index} 
        index={index} 
        value={value} 
        handle={onChangeHandle}
        isSelected={dummy}
        />
      )
      })}
    </tbody>
    </table>
    <div>
      {selectedChild}
    </div>
  </div>)
}

function Child({index, value, handle, isSelected }) {
  console.log('rendering')

 return (
 <tr>
    <td>
      <input 
      type="checkbox" 
      checked={isSelected(index)}
      onChange={(event) => handle(event, index)}/>
    </td>
    <td>hello {index} {value}</td>
 </tr>
 )
}

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

Current behaviour: In above code, When i m clicking on the checkbox 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 row have to go for re-render

Demo: https://codesandbox.io/s/newpro-0pezc

for that you can use React.memo that will memoize your component if props remains the same. But given your code you need to make some extra changes:

  • you have to apply useCallback to memoize onChangeHandle function;

  • to memoize properly onChangeHandle you need to refactor it. you can't pass selectedChild directly, otherwise it memoizes its value. use setSelectedChild passing as argument a function that takes selectedChild instead.

  • your Child should receive isSelected as boolean value instead of function. otherwise props will remain the same and Child never updates;

    import React, { useState, memo, useCallback } from "react";
    
    function Parent() {
      const [selectedChild, setSelectedChild] = useState([]);
    
      const onChangeHandle = useCallback((event, id) => {
        setSelectedChild(selectedChild => {
          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);
            }
          }
          return updatedArray;
        });
      }, []);
    
      const dummy = id => {
        return selectedChild.includes(id);
      };
    
      const renderChildren = () =>
        [1, 2, 3].map((value, index) => {
          return (
            <Child
              key={index}
              index={index}
              value={value}
              handle={onChangeHandle}
              isSelected={dummy(index)}
            />
          );
        });
    
      return (
        <div>
          <table>
            <tbody>{renderChildren()}</tbody>
          </table>
          <div>{selectedChild}</div>
        </div>
      );
    }
    
    const Child = memo(({ index, value, handle, isSelected }) => {
      console.log("rendering");
    
      return (
        <tr>
          <td>
            <input
              type="checkbox"
              checked={isSelected}
              onChange={event => handle(event, index)}
            />
          </td>
          <td>
            hello {index} {value}
          </td>
        </tr>
      );
    });
    
    export default function App() {
      return (
        <div className="App">
          <Parent />
        </div>
      );
    }
    

https://stackblitz.com/edit/so-memo-children?file=src/App.js

登录 关闭
扫码关注1秒登录
如何避免在父组件状态更新时重新渲染循环中的所有子组件
发送“验证码”获取 | 15天全站免登陆