如何使用Immer根据之前的状态设置React组件状态?



我正试图找出如何设置我的React组件的状态基于以前的状态,同时也使用Immer库允许更容易的不可变状态更改。

通常,如果没有Immer,我可以使用setState()updater函数版本安全地访问组件的先前状态:

this.setState(prevState => {
prevState.counter = prevState.counter + 1;
});

然而,当使用Immer库允许更容易的不可变状态更改时,您不再能够访问更新函数或prevState变量。对于大多数操作来说,这是很好的,因为Immerdraft代理可以用于简单的更改-但它不适用于更复杂的更改,如比较,因为代理不反映原始状态对象:

this.setState(produce(draft => {
const index = draft.list.indexOf(item); // doesn't work - always returns -1
if (index > -1) {
draftState.list.splice(index, 1);
}
});

问题是,由于draft状态是一个代理,比较(如indexOf)总是失败,因为这两个对象本质上是不同的。但我不想只是使用this.state在我的生产功能,因为React文档是非常清楚的,你不应该依赖于它的值计算新状态时,我不想做没有Immer状态更新,复杂的状态对象的变化是明显更简单时使用可变代理对象。

是否有任何方法可以访问组件以前状态的最新版本,同时在我的setState调用中仍然使用Immerproduce函数?


编辑:更新以添加我的实际代码的(简化)示例:

constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
fooBar: {
selectedOptions: []
}
};
}
handleClick(clickedOption) {
/*
if clickedOption is already selected
remove option from selectedOptions
else
add option to selectedOptions
*/
this.setState(produce(draft => {
const index = draft.fooBar.selectedOptions.indexOf(clickedOption);    // The problem is here
if (index > -1) {
draft.fooBar.selectedOptions.splice(index, 1);
}
else {
draft.fooBar.selectedOptions.push(clickedOption);
}
}));
}

问题在const index = ...行。如果我使用draft,那么indexOf函数总是返回-1,如果我使用this.state,那么我不能保证selectedOptions的最新版本来比较…

您可以自己实现usePrevious()钩子来存储任何先前的变量:

function usePrevious(value) {
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}

最新更新