Multiple render issues using useState
It is common to write the following code when using hooks, and then find the page rendered twice, sometimes with more headaches.
const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(async() = > {const res = await axios.get("xxx");
setLoading(false); setData(res); } []);Copy the code
In React, synchronous code merges renderings, while asynchronous code does not.
The following code will render once only, and it will merge the setLoading and setData. This is the same as a class component in that it does not incorporate setState in asynchronous functions.
const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(() = > {
setLoading(false);
setData({ a: 1}); } []);Copy the code
Class components that handle multiple renders are easier, but hooks are troublesome.
Method 1: Merge multiple states into one state
To solve the problem of multiple renders, put all dependency states into one object and set setState together, 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 }); } []);Copy the code
The problem with this is that if you want to setState only one dependency, you need to pass in other dependencies as well, otherwise the value will be lost. React doesn’t do merging for you internally.
setRequest({ data: res }); // The loading value is lost.
Copy the code
The solution is to use extension operators
setRequest({ ... request,data: res });
/ / or
setRequest((prevState) = > ({ ...prevState, data: res }));
Copy the code
Method 2: Write a custom merge dependencyhook
It’s too cumbersome to use the extension operation to match and depend on each setState.
Use the React custom hook function to write a useMergeState hook to merge dependencies.
Custom hooks need to start with use.
const useMergeState = (initialState) = > {
const [state, setState] = useState(initialState);
const setMergeState = (newState) = >
setState((prevState) = > ({ ...prevState, newState }));
return [state, setMergeState];
};
/ * * / use
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}});// The loading state will not be lost} []);Copy the code
Plan 3: UseuseReducer
React provides useReducer to manage dependencies, rather than 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}});// The loading state will not be lost} []);Copy the code
If you want to get the previous 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) = > {
console.log(prevState)
return { loading: true.data: res }
});
// ...
setRequest({ data: { a: 1}});// The loading state will not be lost} []);Copy the code
Refer to the article: stackoverflow.com/questions/5…