简体   繁体   English

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

[英]How to avoid rerender all child components which in loop when parent component state update

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.当其中一个子组件更新父组件的 state 时,它会重新渲染所有子组件,因为它是循环的。 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 im clicking on the checkbox in one of the children component, it is updating the parent component state( selectedChild ).当前行为:在上面的代码中,当我单击其中一个子组件中的复选框时,它正在更新父组件状态( 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预期行为:只有特定行必须 go 才能重新渲染

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

for that you can use React.memo that will memoize your component if props remains the same.为此,如果道具保持不变,您可以使用React.memo来记忆您的组件。 But given your code you need to make some extra changes:但是鉴于您的代码,您需要进行一些额外的更改:

  • you have to apply useCallback to memoize onChangeHandle function;你必须应用useCallback来记忆 onChangeHandle function;

  • to memoize properly onChangeHandle you need to refactor it.要正确记住 onChangeHandle,您需要对其进行重构。 you can't pass selectedChild directly, otherwise it memoizes its value.你不能直接传递selectedChild ,否则它会记住它的值。 use setSelectedChild passing as argument a function that takes selectedChild instead.使用setSelectedChild作为参数传递一个 function 来代替selectedChild

  • your Child should receive isSelected as boolean value instead of function.您的孩子应该收到isSelected作为 boolean 值而不是 function。 otherwise props will remain the same and Child never updates;否则 props 将保持不变,并且 Child 永远不会更新;

     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.jshttps://stackblitz.com/edit/so-memo-children?file=src/App.js

The basic answer is use React.memo on Child .基本答案是在Child上使用React.memo

const Child = memo(function Child(...) {...})

But to make memo work, the component needs to receive the same props if it shouldn't get rerendered.但是为了让memo工作,如果它不应该被重新渲染,组件需要接收相同的道具。 That means using useCallback on onChangeHandle :这意味着在onChangeHandle上使用useCallback

 const onChangeHandle = useCallback((event, id) => {...}, [])

But since onChangeHandle uses selectedChild that always changes on checkbox change, you'll also need to ref it using useRef :但是由于onChangeHandle使用selectedChild总是在复选框更改时更改,因此您还需要使用useRef来引用它:

const selectedChildRef = useRef();
selectedChildRef.current = selectedChild;

and use reffed version inside of onChangeHandle .并在onChangeHandle内部使用 refed 版本。

The last thing that needs to be done is to change isSelected prop from function to just a flag since it needs to be run on each checkbox change:最后需要做的是将isSelected属性从 function 更改为一个标志,因为它需要在每个复选框更改时运行:

isSelected={selectedChild.includes(index)}

https://codesandbox.io/s/newpro-forked-wxvqs https://codesandbox.io/s/newpro-forked-wxvqs

You could implement shouldComponentUpdate (doc: https://reactjs.org/docs/react-component.html#shouldcomponentupdate ) inside the definition of Child to have more control over when it rerenders.您可以在 Child 的定义中实现 shouldComponentUpdate (文档: https://reactjs.org/docs/react-component.html#shouldcomponentupdate )以更好地控制它何时重新呈现。 But that's only meant for cases where you have performance issues- generally you don't have to worry about it, and letting them all rerender is standard.但这仅适用于您遇到性能问题的情况 - 通常您不必担心它,让它们全部重新渲染是标准的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从子组件更改 state 时,反应避免重新渲染父组件 - React avoid rerender of parent component when changing state from child component React:避免在状态更改时重新渲染子组件 - React: avoid child component rerender on state change 如何将所有状态值传递给子组件数并更新父组件 - How to pass all state values to number of child components and update the parent 通过父组件过滤子组件时如何维护子组件的state? - How to maintain state of child components when they are filtered through the parent component? 当子组件中发生onclick事件时,如何重新呈现父组件? - how to rerender parent component when onclick event happend in child component? 反应无状态子组件不会在父组件的状态更改时更新 - react stateless child components do not update on state change in parent component 当父组件中发生事件时,如何从子组件更新父组件中的 state - How do you update state in parent component from a child component when an event takes place in the parent component 子组件不会重新渲染,但父组件会重新渲染。 如何让子组件重新渲染? - Child component doesn't rerender but parent component does rerender. How to make child component rerender? 如何使用父级(类组件)的上下文值更新子组件中的 state - How to update state in child component with context value from parent (class components) 如何在父组件中调用 http 到 setState,然后将 state 发送到所有子组件? - How to make an http call in parent component to setState and then send the state to all child components?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM