Continuing our series about React, specifically React hooks, let’s take a look at the useEffect
hook. This is a very powerful hook that allows you to handle side effects of state or prop changes as well as creating on-mount functionality similar to componentDidMount
in class components.
As such, this hook is frequently used for asynchronous requests like loading data for a component as soon as that component is rendered or loading new data based off another value changing. Despite its many uses, creating this sort of functionality can be tricky and you may end up running into a warning like useEffect must not return anything besides a function, which is used for clean-up
.
As mentioned previously, wanting to do something asynchronously inside of useEffect
is a common use-case for this hook. For example, imagine you have a component that shows a list of users but those users come from a database so you must request them on page load. In its simplest form, you might have a component like this:
const UsersComponent = () => {
const [users, setUsers] = useState([]);
const [usersLoading, setUsersLoading] = useState(false);
useEffect(async () => {
setUsersLoading(true);
// getUsers is your api call function that returns a promise
const result = await getUsers();
setUsers(result);
setUsersLoading(false);
// no values in the dependency array means this effect will run once when the component mounts
}, []);
return (
<div>
<h1>Users</h1>
<div>
{users.map((user) => (
<div key={user.id}>{user.username}</div>
))}
</div>
</div>
);
};
Code language: JavaScript (javascript)
Here you can see the basic setup for the component described above. When the page loads, the component should load the users from an API and once that is finished it will update its state accordingly. Looks great, but this will trigger the useEffect must not return anything besides a function, which is used for clean-up
warning mentioned above. That is because the callback itself is returning a promise which is incompatible with useEffect
.
The fix here is actually quite simple. You should wrap your asynchronous work in a function and then just call that from the useEffect
callback. Here is that solution using the example above:
const UsersComponent = () => {
const [users, setUsers] = useState([]);
const [usersLoading, setUsersLoading] = useState(false);
useEffect(() => {
// wrap your async call here
const loadData = async () => {
setUsersLoading(true);
const result = await getUsers();
setUsers(result);
setUsersLoading(false);
};
// then call it here
loadData();
}, []);
return (
<div>
<h1>Users</h1>
{usersLoading && <div>Loading...</div>}
<div>
{users.map((user) => (
<div key={user.id}>{user.username}</div>
))}
</div>
</div>
);
};
Code language: JavaScript (javascript)
Keep in mind that even though this works you should probably refactor your solution further to handle race-conditions and cleanup of your asynchronous calls but that is outside of the scope of this article.
React hooks, asynchronous calls, and side effects are all complicated topics so combining them can be tricky to say the least. Hopefully this solution helps with your use case and, as always, feel free to leave any feedback you may have. Thanks for reading!
Many Thanx!!!! This helped me tremendously