更改数组中的一个状态会导致在 React Hooks 中重新渲染整个循环生成的自定义组件

Posted

技术标签:

【中文标题】更改数组中的一个状态会导致在 React Hooks 中重新渲染整个循环生成的自定义组件【英文标题】:Changing one state in an array causes a re-rendering of the entire loop-generated custom components in React Hooks 【发布时间】:2020-04-18 16:11:16 【问题描述】:

我正在使用 React Hooks。我正在从地图中填充按钮列表。每个按钮在disabled 属性上都有自己的状态。填充它们后,当单击按钮时,我想将该按钮设置为禁用。

const initialBtnDisabled = [false, false, false, false, false, false, false, false];
const [btnDisabled, setBtnDisabled] = useState(initialBtnDisabled);

const onChange = event => 
  const btnIndex = event.target.value;

  let btnDisabledcopy = [...btnDisabled]
  btnDisabledcopy[btnIndex] = true
  setBtnDisabled(btnDisabledcopy)        


const Button = (props) => 
  console.log("I am from button");

  const  btnIndex  = props
    return (
      <button onClick=onChange value=btnIndex disabled=btnDisabled[btnIndex]>click me</button>
          )


const btnArr = [1, 2, 3, 4, 5, 6, 7, 8]
const btnFields = btnArr.map((item, index) =>
  <td key=index>
    <Button btnIndex=index />
  </td>
);

return (
  <tr>btnFields</tr>
)

现在这可行,但问题在于每次单击按钮时,Button 组件都会再次为整个循环重新渲染,而我只更改了数组的一个状态。我在Button 组件中的console.log 每次点击都会记录8 次。

如何防止这种情况发生?

【问题讨论】:

只需在 Button 组件中使用标量禁用状态值 【参考方案1】:

您可以使用 React PureComponent 防止重新渲染。 如果传递的 props 被更新,这只会重新渲染组件。

const initialBtnDisabled = [false, false, false, false, false, false, false, false];
const [btnDisabled, setBtnDisabled] = useState(initialBtnDisabled);

const onChange = event => 
  const btnIndex = event.target.value;

  let btnDisabledcopy = [...btnDisabled]
  btnDisabledcopy[btnIndex] = true
  setBtnDisabled(btnDisabledcopy)        


class Button extends React.PureComponent 
  render () 
    console.log("I am from button");
    const  btnIndex  = this.props;
    return (
      <button onClick=onChange value=btnIndex disabled=btnDisabled[btnIndex]>click me</button>
    )
  


const btnArr = [1, 2, 3, 4, 5, 6, 7, 8]
const btnFields = btnArr.map((item, index) =>
  <td key=index>
    <Button btnIndex=index />
  </td>
);

return (
  <tr>btnFields</tr>
)

【讨论】:

这仍然重新渲染 @reddy 单击一个按钮是否仍然记录 8 次? 是的,每次点击按钮 @reddy 我的错,我在评论中提到使用 PureComponent 但我在编写代码时使用了 React.Component。我现在已经更新了代码。我希望现在可以使用:/ 还是一样,是不是因为我的 btnDisabled 状态是一个数组?【参考方案2】:

您的按钮正在重新渲染,因为它们的道具正在改变。

通过将Button 定义移到渲染函数之外,您可以像这样实现:

const Button = memo(props => 
  console.log('I am from button')

  const  btnIndex, onClick, disabled  = props
  return (
    <button
      onClick=() => onClick(btnIndex)
      value=btnIndex
      disabled=disabled
    >
      click me
    </button>
  )
)

function App() 
  const initialBtnDisabled = [
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false,
  ]
  const [btnDisabled, setBtnDisabled] = useState(initialBtnDisabled)
  const btnDisabledRef = useRef(btnDisabled)

  btnDisabledRef.current = btnDisabled

  const onClick = useCallback(btnIndex => 
    let btnDisabledcopy = [...btnDisabledRef.current]
    btnDisabledcopy[btnIndex] = true
    setBtnDisabled(btnDisabledcopy)
  , [])

  const btnArr = [1, 2, 3, 4, 5, 6, 7, 8]
  const btnFields = btnArr.map((item, index) => (
    <td key=index>
      <Button
        btnIndex=index
        onClick=onClick
        disabled=btnDisabled[index]
      />
    </td>
  ))

  return <tr>btnFields</tr>


const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

注意使用useRef 来跟踪状态的当前值。这允许我们将[] 指定为依赖项

【讨论】:

以上是关于更改数组中的一个状态会导致在 React Hooks 中重新渲染整个循环生成的自定义组件的主要内容,如果未能解决你的问题,请参考以下文章

如何渲染类型为对象数组的 React Hook 状态?

React hook查询不会在状态更改时更新

React Typescript 尝试更改数组对象的状态导致错误

如何在 React 中使用 useState Hook 更改/添加数组的某一行的值

如何更改数组状态容器中的对象属性? React Hooks 使用状态

react之Hook的useEffect详解