-
Notifications
You must be signed in to change notification settings - Fork 48.6k
Async useEffect is pretty much unreadable #14326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I think you're making it more complicated than it needs to be. useEffect(() => {
async function fetchMyAPI() {
let url = 'http://something';
let config = {};
const response = await myFetch(url);
console.log(response);
}
fetchMyAPI();
}, []); If it uses some prop like useEffect(() => {
async function fetchMyAPI() {
let url = 'http://something/' + productId;
let config = {};
const response = await myFetch(url);
console.log(response);
}
fetchMyAPI();
}, [productId]); However, note that this doesn't guarantee requests come in order. So a better solution would be: useEffect(() => {
let didCancel = false;
async function fetchMyAPI() {
let url = 'http://something/' + productId;
let config = {};
const response = await myFetch(url);
if (!didCancel) { // Ignore if we started fetching something else
console.log(response);
}
}
fetchMyAPI();
return () => { didCancel = true; }; // Remember if we start fetching something else
}, [productId]); This is a good article that goes into more detail and has more useful examples: https://www.robinwieruch.de/react-hooks-fetch-data/ In longer term, we'll recommend Suspense for data fetching which will look more like const response = MyAPIResource.read(); and no effects. But in the meantime the code above works fine. |
What if I need to close over // Elsewhere
async function fetchWidgets() {
return await fetch('./api/widgets').then(r => r.json())
}
// In my component:
function MyComponent() {
const [widgets, setWidgets] = useState([]);
// Need another async wrapper function to close over `setWidgets`?
useEffect(() => (async () => setWidgets(await fetchWidgets()))()); (Or based on your example): function MyComponent() {
const [widgets, setWidgets] = useState([]);
async function unnecessaryApiWrapperClosureThatAddsLayersOfIndirection() {
setWidgets(await fetchWidgets());
}
useEffect(() => unnecessaryApiWrapperClosureThatAddsLayersOfIndirection()); Also where do the errors go that are thrown in that async function invocation? I'd need to useEffect(() => (async () => {
try {
setWidgets(await fetchWidgets());
catch e {
setError(e);
}
})()); Why not make useEffect take an async function if we'd have to wrap them all the time anyways? |
I guess one can always just function MyComponent() {
const [widgets, setWidgets] = useState([]);
useEffect(() => {
fetchWidgets.then(w => setWidgets(w))
}); |
@gaearon Scenario "initial unique call to a server": it' means , this it the best practise to getData useEffect(() => {
console.log("useEffect, fetchData here");
}, []); Full Code here import React, { useState, useEffect } from 'react';
export function CaseAssignDropDown() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("useEffect, fetchData here");
}, []);
/*
[] -> get data once
[count] or removed [] -> get data each click
*/
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
} https://stackoverflow.com/questions/54596192/react-hooks-async-best-practise |
Please ask on StackOverflow and feel free to link to your question from here. We don’t use issues for support because answers get lost on GitHub. |
ref: https://reactjs.org/docs/hooks-intro.html As for async API call, I followed the suggestion facebook/react#14326 (comment)
|
@codeaid It is an intentional design decision. The async/await model is a pitfall for effects in general (and has always been a pitfall in classes!) due to race conditions. In the longer term, Suspense will be the recommended data fetching solution, and you can do |
As I explained above, This kind of code would handle it correctly: useEffect(() => {
let didCancel = false;
async function fetchMyAPI() {
let url = 'http://something/' + productId;
let config = {};
const response = await myFetch(url);
if (!didCancel) { // Ignore if we started fetching something else
console.log(response);
}
}
fetchMyAPI();
return () => { didCancel = true; }; // Remember if we start fetching something else
}, [productId]); I'm going to lock this issue as resolved to prevent further confusion. Here's a good article that answers your other questions (e.g. about loading states): https://www.robinwieruch.de/react-hooks-fetch-data/ |
Uh oh!
There was an error while loading. Please reload this page.
The only alternative is using
then()
chains which aren't very readable at their own.The text was updated successfully, but these errors were encountered: