哪个是使用状态挂钩更新状态对象的最佳实践?
正如其他答案所指出的那样,它们都是有效的。
有什么区别?
似乎混乱是由于"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] });
}
...
}
他们都propA/B
在toggle
处理程序中切换。他们都只更新了一个作为e.target.name
.
看看当您只更新setMyState
.
以下演示显示单击propA
会引发错误(仅发生setMyState
),
你可以跟着
警告:组件正在将复选框类型的受控输入更改为不受控制。输入元素不应从受控切换到不受控制(反之亦然)。决定在组件的生命周期内使用受控或非受控输入元素。
这是因为当您单击propA
复选框时,propB
值会被删除,并且只有propA
值会被切换,从而使propB
的checked
值未定义,从而使复选框不受控制。
并且this.setState
一次只更新一个属性,但更新merges
其他属性,因此复选框保持受控。
我挖掘了源代码,行为是由于useState
调用useReducer
在内部,useState
调用useReducer
,它返回减速器返回的任何状态。
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
});
因此,如果您只设置一个属性,则不会合并其他属性。