我正在做这个练习: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 种不同的方法来做到这一点,但我不确定为什么它不起作用。我想完全理解为什么。并查看一个用最简单的代码编写的解决方案,以便我可以参考它。当我说简单时,我的意思是最好没有花哨的语法或扩展运算符。只是为了得到一个想法。谢谢大家。
在您的版本中:
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 会观察到数组引用不同并且状态已更新。
第二件事要注意的是,这个数组由两部分组成:
因此,总的来说,这里的要点是,在更新状态时,您应该始终创建需要修改的任何对象或数组的新实例,而不是修改当前的实例。