React Hooks Set 函数:如何正确更新对象数组中的属性?

问题描述 投票:0回答:1

我正在做这个练习:https://www.reacterry.com/portal/challenges/select-all-checkboxes

如果您查看 checkOnChange 函数,我尝试以多种不同的方式执行此操作,但它们似乎不起作用,我想真正了解原因。

我只是对 setCheckboxes 部分感到困惑。

他们的:

setCheckboxes(
    checkboxes.map((checkbox) =>
      checkbox.id === id
        ? { ...checkbox, checked: !checkbox.checked }
        : checkbox
    )
  );

我的:


//WHY DOESNT THIS WORK?
    const arr = checkboxes;
    const index = arr.findIndex((x) => x.id === id);
    console.log(index);
    console.log(arr);
    arr[index].checked = !arr[index].checked;
    setCheckboxes(arr);
    console.log(checkboxes);

    //WHY DOESNT THIS WORK?
    setCheckboxes((prevState) => {
      const arr = prevState;
      const index = arr.findIndex((x) => x.id === id);
      console.log(index);
      console.log(arr);
      arr[index].checked = !arr[index].checked;
      return arr;
    });


    //WHY DOESNT THIS WORK?
    setCheckboxes(
      checkboxes.forEach((checkbox) => {
        if (checkbox.id === id) {
          checkbox = { ...checkbox, checked: !checkbox.checked }
        }
      })
    );`

我不完全理解我的方式和这种方式之间的区别的解决方案:

import React, { useState } from 'react';
import styled from 'styled-components';

const CheckboxList = () => {
const [checkboxes, setCheckboxes] = useState([
  { id: 1, label: 'Dogs', checked: false },
  { id: 2, label: 'Cats', checked: false },
  { id: 3, label: 'Cows', checked: false },
  { id: 4, label: 'Deers', checked: false },
]);

const handleCheckboxChange = (id) => {
  setCheckboxes(
    checkboxes.map((checkbox) =>
      checkbox.id === id
        ? { ...checkbox, checked: !checkbox.checked }
        : checkbox
    )
  );
};

const handleSelectAll = () => {
  setCheckboxes(checkboxes.map((checkbox) => ({ ...checkbox, checked: true })));
};

return (
  <Container>
    <CheckboxContainer data-testid="checkbox-container">
      {checkboxes.map((checkbox, index) => (
        <CheckboxLabel key={checkbox.id}>
          <input
            data-testid={`checkbox-${index + 1}`}
            type="checkbox"
            checked={checkbox.checked}
            onChange={() => handleCheckboxChange(checkbox.id)}
          />
          {checkbox.label}
        </CheckboxLabel>
      ))}
    </CheckboxContainer>
    <SelectAllButton data-testid="button" onClick={handleSelectAll}>Select All</SelectAllButton>
  </Container>
);
};

export default CheckboxList;

const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin: 24px;
`;

const CheckboxContainer = styled.div`
  display: flex;
  flex-direction: column;
  gap: 5px;
`;

const CheckboxLabel = styled.label`
  display: flex;
  align-items: center;
  gap: 5px;
`;

const SelectAllButton = styled.button`
  padding: 10px 20px;
  font-size: 18px;
  border: none;
  border-radius: 4px;
  background-color: #333;
  color: #fff;
  cursor: pointer;
  margin-top: 24px;

  &:hover {
    opacity: 0.8;
  }
`;

我的代码如下:

import React, { useState, useEffect } from 'react';
import styled from 'styled-components';

const CheckboxList = () => {
  const [checkboxes, setCheckboxes] = useState([
    { id: 1, label: 'Dogs', checked: false },
    { id: 2, label: 'Cats', checked: false },
    { id: 3, label: 'Cows', checked: false },
    { id: 4, label: 'Deers', checked: false },
  ]);

  const checkedOnChange = (id) => {

    //WHY DOESNT THIS WORK?
    const arr = checkboxes;
    const index = arr.findIndex((x) => x.id === id);
    console.log(index);
    console.log(arr);
    arr[index].checked = !arr[index].checked;
    setCheckboxes(arr);
    console.log(checkboxes);

    //WHY DOESNT THIS WORK?
    setCheckboxes((prevState) => {
      const arr = prevState;
      const index = arr.findIndex((x) => x.id === id);
      console.log(index);
      console.log(arr);
      arr[index].checked = !arr[index].checked;
      return arr;
    });


    //WHY DOESNT THIS WORK?
    setCheckboxes(
      checkboxes.forEach((checkbox) => {
        if (checkbox.id === id) {
          checkbox = { ...checkbox, checked: !checkbox.checked }
        }
      })
    );
  };

  return (
    <Container>
      <CheckboxContainer data-testid="checkbox-container">
        {checkboxes.map((checkbox, index) => (
          <CheckboxLabel key={checkbox.id}>
            <input
              data-testid={`checkbox-${index + 1}`}
              type="checkbox"
              checked={checkbox.checked}
              onClick={() => {
                checkedOnChange(checkbox.id);
              }}
            />
            {checkbox.label}
          </CheckboxLabel>
        ))}
      </CheckboxContainer>
      <SelectAllButton data-testid="button">Select All</SelectAllButton>
    </Container>
  );
};

export default CheckboxList;

const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin: 24px;
`;

const CheckboxContainer = styled.div`
  display: flex;
  flex-direction: column;
  gap: 5px;
`;

const CheckboxLabel = styled.label`
  display: flex;
  align-items: center;
  gap: 5px;
`;

const SelectAllButton = styled.button`
  padding: 10px 20px;
  font-size: 18px;
  border: none;
  border-radius: 4px;
  background-color: #333;
  color: #fff;
  cursor: pointer;
  margin-top: 24px;

  &:hover {
    opacity: 0.8;
  }
`;

我尝试了 3 种不同的方法来做到这一点,但我不确定为什么它不起作用。我想完全理解为什么。并查看一个用最简单的代码编写的解决方案,以便我可以参考它。当我说简单时,我的意思是最好没有花哨的语法或扩展运算符。只是为了得到一个想法。谢谢大家。

javascript reactjs react-hooks ecmascript-6 state
1个回答
0
投票

在您的版本中:

setCheckboxes((prevState) => {
  const arr = prevState;
  const index = arr.findIndex((x) => x.id === id);
  console.log(index);
  console.log(arr);
  arr[index].checked = !arr[index].checked;
  return arr;
});

返回的

arr
prevState
是相同的数组实例。该数组中的数据已被突变(在 React 状态下不应该这样做),并且数组本身是相同的引用(这意味着 React 无法看到状态已更改)。

您的所有版本本质上都是如此。您正在深入研究数组并修改其内容,并返回相同的数组。

将此与他们的版本进行对比:

setCheckboxes(
  checkboxes.map((checkbox) =>
    checkbox.id === id
      ? { ...checkbox, checked: !checkbox.checked }
      : checkbox
  )
);

这里首先要注意的是,

.map()
的返回值被设置为新状态,并且
.map()
创建了一个new数组。因此 React 会观察到数组引用不同并且状态已更新。

第二件事要注意的是,这个数组由两部分组成:

  1. 对于任何不符合条件的元素,请使用相同的元素(对象引用)
  2. 对于任何确实匹配条件的元素,创建并返回一个new对象实例(而不是改变现有实例)

因此,总的来说,这里的要点是,在更新状态时,您应该始终创建需要修改的任何对象或数组的新实例,而不是修改当前的实例。

© www.soinside.com 2019 - 2024. All rights reserved.