挂钩设置器未使用对象变量设置状态

时间:2020-06-20 21:32:37

标签: reactjs react-hooks

我正在尝试使用react构建一个简单的树菜单。我发现this video确实显示了我想要实现的目标(his Codepen)。我能够实现他的方法,但是出于好奇,尝试使用钩子复制相同的方法。我最后得到了这个(简体):

my 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),在函数updateSelection中,绝对没有做任何事情(当然也不会触发useEffect)。 我不知道为什么。我在其上方放置了一个console.log,以检查变量(“ sel”)是否正确,但看起来还不错。我尝试对“ sel”的值进行硬编码,{chicken-id:{}},当我这样做时,它就可以工作。

1 个答案:

答案 0 :(得分:0)

问题是您直接在改变状态对象:

    if(selectedOptions[selectedOptionId]){
      delete selectedOptions[selectedOptionId];
    } else {
      selectedOptions[selectedOptionId] = {}      
    }    

    onChange(selectedOptions);

在两种结果中,您都更改了状态对象,然后将其设置为自身,因此useEffect自然不会触发,因为设置器未进行任何实际更改。您必须小心在React中执行此操作,因为这是结束陈旧值的简单方法。仅当当前状态与设置器传递的值之间存在差异时才触发重新渲染,而不是由于任何原因而在状态值发生更改时才触发重新渲染。

一条评论建议您使用传播-...-分解状态对象,以便可以将其设置为自身,但这对我来说似乎有点反模式,尤其是当它离开状态时突变到位。如果要从状态对象中删除条目,则通常的方法是首先克隆对象,对克隆进行突变,然后将其设置为新状态:

    const clone = Object.assign({}, selectedOptions);
    if(clone[selectedOptionId]){
      delete clone[selectedOptionId];
    } else {
      clone[selectedOptionId] = {}      
    };
    onChange(clone);

不过请注意,该对象内的任何嵌套对象都将保留其对原始状态的引用,并且仍可能对其进行突变。您还必须克隆这些嵌套的对象,以避免出现此问题。