我正在尝试用 react 构建一个简单的树形菜单。我发现这个视频准确地显示了我想要实现的目标(他的Codepen(。我能够实现他的方法,但后来出于好奇,试图使用钩子复制相同的方法。我最终得到了这个(简化(:
我的代码笔
const App2 = () => {
const [selectedOptions, setSelectedOptions] = React.useState({});
React.useEffect(() => {
console.log(selectedOptions);
},[selectedOptions]);
const updateSelection = (sel) => {
console.log(sel)
setSelectedOptions(sel);
}
return (
<div className="wrapper">
<h1>Toppings</h1>
<OptionsList
options={options}
onChange={updateSelection}
selectedOptions={selectedOptions}
/>
</div>
);
}
const OptionsList = ({ selectedOptions, onChange }) => {
const handleCheckboxClicked = (selectedOptionId) => {
if(selectedOptions[selectedOptionId]){
delete selectedOptions[selectedOptionId];
} else {
selectedOptions[selectedOptionId] = {}
}
onChange(selectedOptions);
}
return (
<div>
<button onClick={() => (handleCheckboxClicked("chicken-id"))} >
Set Chicken
</button>
</div>
)
}
ReactDOM.render(<App2 />, document.querySelector('#app'));
问题是setSelectedOptions(sel)
,函数 updateSelect 内部绝对没有做任何事情(当然 useEffect 也没有被触发(。 我不知道为什么。我在它上面放了一个控制台.log以检查变量("sel"(是否正常,但似乎没问题。我尝试硬编码"sel"的值,{chicken-id:{}},当我这样做时它可以工作。
问题是你直接改变了状态对象:
if(selectedOptions[selectedOptionId]){
delete selectedOptions[selectedOptionId];
} else {
selectedOptions[selectedOptionId] = {}
}
onChange(selectedOptions);
在这两种结果中,您都更改了状态对象,然后将其设置为自身,因此自然不会触发useEffect
,因为 setter 没有实际更改。在 React 中这样做时必须小心,因为这是一种以过时值结束的简单方法。仅当当前状态与资源库传递的值之间存在差异时,才会触发重新渲染,而不是在状态值出于任何原因更改时触发。
一条评论建议您使用 spread -...
- 来解构状态对象,以便您可以将其设置为自身,但这对我来说似乎有点反模式,尤其是当它保留状态突变时。如果要从状态对象中删除条目,则一般方法是先克隆对象,更改克隆,然后将其设置为新状态:
const clone = Object.assign({}, selectedOptions);
if(clone[selectedOptionId]){
delete clone[selectedOptionId];
} else {
clone[selectedOptionId] = {}
};
onChange(clone);
但需要注意的是,此对象中的任何嵌套对象都将保留其对原始状态的引用,并且仍可能使其发生变化。您还必须克隆这些嵌套对象以避免此问题。