哪一个是使用状态钩子更新状态对象的最佳实践?
正如其他答案所指出的那样,它们都是有效的。
有什么区别?
似乎混淆是由于"Unlike the setState method found in class components, useState does not automatically merge update objects",尤其是“合并”部分。
让我们比较一下this.setState和useState
class SetStateApp extends React.Component {
state = {
propA: true,
propB: true
};
toggle = e => {
const { name } = e.target;
this.setState(
prevState => ({
[name]: !prevState[name]
}),
() => console.log(`this.state`, this.state)
);
};
...
}
function HooksApp() {
const INITIAL_STATE = { propA: true, propB: true };
const [myState, setMyState] = React.useState(INITIAL_STATE);
const { propA, propB } = myState;
function toggle(e) {
const { name } = e.target;
setMyState({ [name]: !myState[name] });
}
...
}
它们都在toggle 处理程序中切换propA/B。
他们都只更新了一个作为e.target.name传递的道具。
查看仅更新 setMyState 中的一个属性时所产生的差异。
以下演示显示单击propA 会引发错误(仅发生setMyState),
你可以跟随
警告:组件正在将复选框类型的受控输入更改为不受控制。输入元素不应从受控切换到不受控(反之亦然)。决定在组件的生命周期内使用受控输入元素还是不受控输入元素。
这是因为当您单击propA 复选框时,propB 值被删除,只有propA 值被切换,从而使propB 的checked 值未定义,从而使复选框不受控制。
并且this.setState 一次只更新一个属性,但它merges 其他属性因此复选框保持控制。
我通过源代码挖掘,行为是由于useState调用useReducer
在内部,useState 调用 useReducer,它返回 reducer 返回的任何状态。
https://github.com/facebook/react/blob/2b93d686e3/packages/react-reconciler/src/ReactFiberHooks.js#L1230
useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
currentHookNameInDev = 'useState';
...
try {
return updateState(initialState);
} finally {
...
}
},
其中updateState 是useReducer 的内部实现。
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, (initialState: any));
}
useReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
currentHookNameInDev = 'useReducer';
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateReducer(reducer, initialArg, init);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
},
如果你熟悉 Redux,你通常会像在选项 1 中所做的那样,通过扩展之前的状态来返回一个新对象。
setMyState({
...myState,
propB: false
});
所以如果你只设置一个属性,其他属性不会合并。