我第一次尝试React钩子,一切似乎都很好,直到我意识到,当我获得数据并更新两个不同的状态变量(数据和加载标志)时,我的组件(数据表)被渲染了两次,即使两个对状态更新器的调用都发生在同一个函数中。这是我的api函数,它将两个变量返回给我的组件。
const getData = url => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(async () => {
const test = await api.get('/people')
if(test.ok){
setLoading(false);
setData(test.data.results);
}
}, []);
return { data, loading };
};
在一个普通的类组件中,你只需要一次调用就可以更新一个复杂的对象的状态,但“钩子方式”似乎是将状态分割成更小的单元,其副作用似乎是当它们分别更新时需要多次重新渲染。有什么办法可以缓解这种情况吗?
除了Yangshun Tay的答案,你最好记住setMergedState函数,这样它每次渲染都会返回相同的引用,而不是新函数。如果TypeScript linter强迫你在useCallback或父组件useEffect中传递setMergedState作为依赖项,这可能是至关重要的。
import {useCallback, useState} from "react";
export const useMergeState = <T>(initialState: T): [T, (newState: Partial<T>) => void] => {
const [state, setState] = useState(initialState);
const setMergedState = useCallback((newState: Partial<T>) =>
setState(prevState => ({
...prevState,
...newState
})), [setState]);
return [state, setMergedState];
};
补充一点回答https://stackoverflow.com/a/53575023/121143
太酷了!对于那些计划使用这个钩子的人来说,它可以用一种有点健壮的方式来使用function作为参数,比如:
const useMergedState = initial => {
const [state, setState] = React.useState(initial);
const setMergedState = newState =>
typeof newState == "function"
? setState(prevState => ({ ...prevState, ...newState(prevState) }))
: setState(prevState => ({ ...prevState, ...newState }));
return [state, setMergedState];
};
更新:优化版本,当传入的部分状态没有改变时,状态不会被修改。
const shallowPartialCompare = (obj, partialObj) =>
Object.keys(partialObj).every(
key =>
obj.hasOwnProperty(key) &&
obj[key] === partialObj[key]
);
const useMergedState = initial => {
const [state, setState] = React.useState(initial);
const setMergedState = newIncomingState =>
setState(prevState => {
const newState =
typeof newIncomingState == "function"
? newIncomingState(prevState)
: newIncomingState;
return shallowPartialCompare(prevState, newState)
? prevState
: { ...prevState, ...newState };
});
return [state, setMergedState];
};