In my link shortening app I was doing everything in one function and now I would like to split it into smaller functions but there is a problem when it comes to fetching. When I fetch data from api, js is waiting for it to be fetched but next functions are being processed before data is saved in a state. It results in component being rendered without needed data because render starts before state is ready. How can I fix it?
const checkLink = () => {
if (!nextLink) {
setErrorMsg("Please add link");
return false;
} else if (!nextLink.match(linkRegex)) {
setErrorMsg("That's not a link");
return false;
} else {
return true;
}
};
const fetchShortenedLink = async () => {
setIsLoading(true);
const response = await fetch(
`https://api.shrtco.de/v2/shorten?url=${nextLink}`
);
if (!response.ok) {
setErrorMsg(`An error has occured: ${response.status}`);
setIsLoading(false);
} else {
const data = await response.json();
await setFetchedLink(data.result.short_link)
setIsLoading(false);
}
};
const shortenLink = async () => {
checkLink();
await fetchShortenedLink();
setLinkArr((prev) => [
...prev,
{
id: prev.length === 0 ? 1 : prev[prev.length - 1].id 1,
long: nextLink,
short: fetchedLink,
},
]);
setNextLink("");
scrollToLink();
};
CodePudding user response:
The "react" way of doing this would be to use useEffect()
and add the state change you want to wait for in the dependency array. Using your own example:
const shortenLink = async () => {
checkLink();
await fetchShortenedLink();
setLinkArr(...);
setNextLink("");
};
useEffect(
() => { scrollToLink() }, // Whatever you want to do after the state changes
[linkArray, nextLink] // Add the state you changed in this dependency array
}
CodePudding user response:
Setting State in React is async and you can't await on it. But you could achieve this by different methods.
You could possibly use a React Ref to store the state of the state variable. Then update the state variable with the react ref. This will render a page refresh, and then use the React Ref in the async function.
const stateRef = React.useRef().current
const [state,setState] = useState(stateRef);
async function some() {
stateRef = { some: 'value' }
setState(stateRef) // Triggers re-render
await some2();
}
async function some2() {
await someHTTPFunctionCall(stateRef.some)
stateRef = null;
setState(stateRef) // Triggers re-render
}