I was trying the useEffect
example something like below:
useEffect(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}, []);
and I get this warning in my console. But the cleanup is optional for async calls I think. I am not sure why I get this warning. Linking sandbox for examples. https://codesandbox.io/s/24rj871r0p
This question is related to
javascript
reactjs
react-hooks
I read through this question, and feel the best way to implement useEffect is not mentioned in the answers. Let's say you have a network call, and would like to do something once you have the response. For the sake of simplicity, let's store the network response in a state variable. One might want to use action/reducer to update the store with the network response.
const [data, setData] = useState(null);
/* This would be called on initial page load */
useEffect(()=>{
fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(data => {
setData(data);
})
.catch(err => {
/* perform error handling if desired */
});
}, [])
/* This would be called when store/state data is updated */
useEffect(()=>{
if (data) {
setPosts(data.children.map(it => {
/* do what you want */
}));
}
}, [data]);
Reference => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
When you use an async function like
async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}
it returns a promise and useEffect
doesn't expect the callback function to return Promise, rather it expects that nothing is returned or a function is returned.
As a workaround for the warning you can use a self invoking async function.
useEffect(() => {
(async function() {
try {
const response = await fetch(
`https://www.reddit.com/r/${subreddit}.json`
);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
})();
}, []);
or to make it more cleaner you could define a function and then call it
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`https://www.reddit.com/r/${subreddit}.json`
);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
the second solution will make it easier to read and will help you write code to cancel previous requests if a new one is fired or save the latest request response in state
Until React provides a better way, you can create a helper, useEffectAsync.js
:
import { useEffect } from 'react';
export default function useEffectAsync(effect, inputs) {
useEffect(() => {
effect();
}, inputs);
}
Now you can pass an async function:
useEffectAsync(async () => {
const items = await fetchSomeItems();
console.log(items);
}, []);
Update
If you choose this approach, note that it's bad form. I resort to this when I know it's safe, but it's always bad form and haphazard.
Suspense for Data Fetching, which is still experimental, will solve some of the cases.
In other cases, you can model the async results as events so that you can add or remove a listener based on the component life cycle.
Or you can model the async results as an Observable so that you can subscribe and unsubscribe based on the component life cycle.
void operator could be used here.
Instead of:
React.useEffect(() => {
async function fetchData() {
}
fetchData();
}, []);
or
React.useEffect(() => {
(async function fetchData() {
})()
}, []);
you could write:
React.useEffect(() => {
void async function fetchData() {
}();
}, []);
It is a little bit cleaner and prettier.
Async effects could cause memory leaks so it is important to perform cleanup on component unmount. In case of fetch this could look like this:
function App() {
const [ data, setData ] = React.useState([]);
React.useEffect(() => {
const abortController = new AbortController();
void async function fetchData() {
try {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url, { signal: abortController.signal });
setData(await response.json());
} catch (error) {
console.log('error', error);
}
}();
return () => {
abortController.abort(); // cancel pending fetch request on component unmount
};
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Please try this
useEffect(() => {
(async () => {
const products = await api.index()
setFilteredProducts(products)
setProducts(products)
})()
}, [])
For other readers, the error can come from the fact that there is no brackets wrapping the async function:
Considering the async function initData
async function initData() {
}
This code will lead to your error:
useEffect(() => initData(), []);
But this one, won't:
useEffect(() => { initData(); }, []);
(Notice the brackets around initData()
try
const MyFunctionnalComponent: React.FC = props => {_x000D_
useEffect(() => {_x000D_
// Using an IIFE_x000D_
(async function anyNameFunction() {_x000D_
await loadContent();_x000D_
})();_x000D_
}, []);_x000D_
return <div></div>;_x000D_
};
_x000D_
Source: Stackoverflow.com