admin管理员组文章数量:1317906
I have the following states
const [humans, setHumans] = useState([]);
const [animales, setAnimals] = useState([]);
And the following function
const f = () => {
console.log(humans);
console.log(animals);
}
which only has to be executed when the following useEffect finishes updating both states
useEffect(() => {
setHumans([ ... ]);
setAnimals([ ... ]);
f();
}, []);
How can I make sure that f is executed after both asynchronous state updates has finished?
I have the following states
const [humans, setHumans] = useState([]);
const [animales, setAnimals] = useState([]);
And the following function
const f = () => {
console.log(humans);
console.log(animals);
}
which only has to be executed when the following useEffect finishes updating both states
useEffect(() => {
setHumans([ ... ]);
setAnimals([ ... ]);
f();
}, []);
How can I make sure that f is executed after both asynchronous state updates has finished?
Share Improve this question asked Mar 21, 2021 at 12:18 RaulRaul 3,0912 gold badges23 silver badges71 bronze badges 4-
3
Why not add a useEffect hook with both humans and animals as dependency, and check your condition there. for eg:
if(humans.length && animals.length)
, then executef()
; – nithinpp Commented Mar 21, 2021 at 12:21 - 1 btw your useEffect will only execute on initial render... – Michael Commented Mar 21, 2021 at 12:24
- You need to execute only when both state updates has finished? – Victor Molina Commented Mar 21, 2021 at 12:34
- When both states has finished being updated. I need to wait for multiple state updates. – Raul Commented Mar 21, 2021 at 12:39
3 Answers
Reset to default 4Try 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.
If you would like to have your state into a Provider and have your fetching logic somewhere else I use this logic, though technically you don't need to divide your code into multiple files.
//Some Provider
const [humans, setHumans] = useState([]);
const [animales, setAnimals] = useState([]);
//Some Component
const {humans} = useHumanStorage();
const {getHumans} = useGetHumans();
const {animals} = useAnimalStorage();
const {getAnimals} = useGetAnimals();
const [isLoaded, setIsLoaded] = useState<boolean>(false);
const f = (humans, animals) => {
console.log(humans);
console.log(animals);
}
useEffect(() => {
f();
}, [isLoaded]);
useEffect(() => {
let promises: Promise<any>[] = [];
promises.push(getAnimals());
promises.push(getHumans());
Promise.all(promises).then(() => {
setIsLoaded(true);
});
}, []);
Example for the architecture: https://github./bespoyasov/frontend-clean-architecture/tree/master/src
You can also do this in one file:
const [isLoaded, setIsLoaded] = useState<boolean>(false);
const [humans, setHumans] = useState([]);
const [animales, setAnimals] = useState([]);
const getAnimals = async () => {
const result = await yourFetchLogic();
setAnimals(result);
}
const getHumans = async () => {
const result = await yourFetchLogic();
setHumans(result);
}
const f = (humans, animals) => {
console.log(humans);
console.log(animals);
}
useEffect(() => {
f();
}, [isLoaded]);
useEffect(() => {
let promises: Promise<any>[] = [];
promises.push(getAnimals());
promises.push(getHumans());
Promise.all(promises).then(() => {
setIsLoaded(true);
});
}, []);
This is not exactly, what you were looking for, but maybe it's good enough:
Instead of waiting for the update, you can just pass the new values to the function explicitly like this:
Change the logging function to this:
const f = (humans, animals) => {
console.log(humans);
console.log(animals);
}
And then simply log the new state independently from the state update:
useEffect(() => {
const newHumans = [ ... ];
const newAnimals = [ ... ];
setHumans(newHumans);
setAnimals(newAnimals);
f(newHumans, newAnimals);
}, []);
This improves readability and was often acceptable behavior for me in the past.
本文标签: javascriptReact HooksWait for multiple state updates to finishStack Overflow
版权声明:本文标题:javascript - React Hooks - Wait for multiple state updates to finish - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742029922a2416251.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论