本文介绍了如何避免在父组件状态更新时重新渲染循环中的所有子组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个位于父组件循环内的子组件.当其中一个子组件正在更新父组件的状态时,它会重新渲染所有子组件,因为它是循环的.如何避免每次迭代重新渲染.

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

</div>)}function Child({index, value, handle, isSelected }) {console.log('渲染')返回 (<tr><td>句柄(事件,索引)}/></td><td>hello {index} {value}</td></tr>)}导出默认函数 App() {返回 (

<父/>

);}

当前行为:在上面的代码中,当我单击子组件之一中的复选框时,它正在更新父组件状态(selectedChild).所以循环正在执行,所有子项(所有表行)都在重新渲染.

预期行为:只有该特定行必须重新渲染

演示: https://codesandbox.io/s/newpro-0pezc

解决方案

为此你可以使用 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

这篇关于如何避免在父组件状态更新时重新渲染循环中的所有子组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-22 03:12