Use useState to render multiple times
When using hooks
, I often write the following code, and then you will find that the page has been rendered twice, which sometimes causes more headaches.
const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(async () => {
const res = await axios.get("xxx");
setLoading(false);
setData(res);
}, []);
In React
, synchronous code will merge rendering, and asynchronous code will not merge rendering.
The following code will only render once, it will setLoading
and setData
. This is actually the same as the class component, setState
will not be merged in the asynchronous function.
const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(() => {
setLoading(false);
setData({ a: 1 });
}, []);
hooks
to solve multiple rendering problems in class components, but it is more troublesome in 061bb5b8c4e9f5.
Method 1: Combine multiple states into one state
Put all the dependent states into one object and setState
together in 061bb5b8c4ea1b to solve the problem of multiple renderings, as shown in the following code
const [request, setRequest] = useState({ loading: true, data: null });
useEffect(async () => {
const res = await axios.get("xxx");
setRequest({ loading: false, data: res });
}, []);
But there is a problem. If you only want setState
, you need to pass in other dependencies as well, otherwise this value will be lost. React
internally will not help you to merge.
setRequest({ data: res }); // loading 值丢失了。
The solution is to use the spread operator
setRequest({ ...request, data: res });
// 或者
setRequest((prevState) => ({ ...prevState, data: res }));
Method 2: Write a custom merged dependency hook
Every time setState
has to use the extended operation conformity and dependency is too troublesome.
Use React
customize the hook
function and write a useMergeState
hook that merges dependencies.
Custom hook
needs to start use
const useMergeState = (initialState) => {
const [state, setState] = useState(initialState);
const setMergeState = (newState) =>
setState((prevState) => ({ ...prevState, newState }));
return [state, setMergeState];
};
/* 使用 */
const [request, setRequest] = useMegeState({ loading: false, data: null });
useEffect(async () => {
const res = await axios.get("xxx");
setRequest({ loading: true, data: res });
// ...
setRequest({ data: { a: 1 } }); // loading 状态不会丢失,还是 true
}, []);
Option 3: Use useReducer
React
provides useReducer
to manage various dependencies instead of using useState
.
const [request, setRequest] = useReducer(
(prevState, newState) => ({ ...prevState, newState }),
{ loading: false, data: null }
);
useEffect(async () => {
const res = await axios.get("xxx");
setRequest({ loading: true, data: res });
// ...
setRequest({ data: { a: 1 } }); // loading 状态不会丢失,还是 true
}, []);
If you want to get the last state, you need to modify the above code.
const [request, setRequest] = useReducer(
(prevState, newState) => {
const newWithPrevState = typeof newState === "function" ? newState(prevState) : newState;
return{ ...prevState, newWithPrevState })
},
{ loading: false, data: null }
);
useEffect(async () => {
const res = await axios.get("xxx");
setRequest((prevState) => {
return { loading: true, data: res }
});
// ...
setRequest({ data: { a: 1 } }); // loading 状态不会丢失,还是 true
}, []);
Reference article: https://stackoverflow.com/questions/53574614/multiple-calls-to-state-updater-from-usestate-in-component-causes-multiple-re-re
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。