Try to use a custom hook "useStateWithCallback" and perform the state updates in a callback chain.
useStateWithCallback
import { useState, useRef, useEffect } from "react";
export default function useStateWithCallback(initialState) {
const [state, setState] = useState(initialState);
const callbackRef = useRef(null);
const setStateCallback = (state, callback) => {
callbackRef.current = callback; // store passed callback to ref
setState(state);
};
useEffect(() => {
if (callbackRef.current) {
callbackRef.current(state);
callbackRef.current = null; // reset callback
}
}, [state]);
return [state, setStateCallback];
}
Code
const [humans, setHumans] = useStateWithCallback([]);
const [animals, setAnimals] =
useStateWithCallback([]);
const logStates = () => {
console.log(humans);
console.log(animals);
}
const updateStates = () => {
return new Promise((resolve) => {
setHumans([...], () => {
setAnimals([...], resolve);
}
});
}
useEffect(() => {
(async () => {
await updateStates();
logAnimals();
})();
}, []);
With this example, you will be able to use the functionality of "waiting for multiple state updates before doing something" outside useEffect too, like this:
const otherFunction = async () => {
await updateStates();
doSomething();
}
If you have problems with not up-to-date states, then wrap the method in a useCallback, adding the states to the deps array.