Overview
Hooks are a newer feature of React, introduced in 2018.
From the documentation:
"Hooks are functions that let you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes."
Hooks utilize JavaScript closures, to maintain access to variables and state inside your components. This is why they are declared inside of your function component.
Basic Hooks
useState
useEffect
useContext
For the purpose of this blog, I will be talking about useEffect.
useEffect
This hook allows you to add side effects from a functional component. For example, a side effect would be fetching data or manually changing the DOM.
From the docs:
"If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined."
This method defaults to run after every render and update, which is like utilizing the lifecycle methods componentDidMount and componentDidUpdate in the same component. However, now you only need to use one method. If you would only like the effect to run on the first render, you can pass it a second argument. Here is how I did this in my current project to make a fetch request from my Rails API:
useEffect(() => {
props.getUsersOfConcert(concertId)
}, [])
The function I passed to useEffect calls an action creator function that fetches usernames from my API. This function then updates my redux state with the received users in order to display their usernames in my component.
Passing an empty array as the second argument to useEffect will ensure that it is only called on the first render and that I don't re-render every time my state is updated. In fact, originally I had left the array out of the arguments and was left with an infinite loop of requesting users, updating state, and re-rendering my component which in turn caused another fetch request and another update of state. Now I understand that useEffect is not exactly like componentDidMount, but rather it is like componentDidMount and componentDidUpdate mixed together.
Here is what the docs have to say about passing an empty array:
"If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works."
If you would like useEffect to check whether the effect should be applied after each render or update instead of just the first render, then all state and props that the render depends on should be passed into the array in the argument to useEffect. React will compare the previous values of the passed in elements to the current values, and if they are different, the effect will run again.
Cleanup
If the side effect you are creating needs cleanup, you can return a cleanup function from the function you pass to useEffect. This returned function will get called when the component un-mounts.
Here is an example from the docs:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
Here, the unsubscribeFromFriendStatus method will be called on the ChatAPI object when the component un-mounts. React will also call this returned function on every render, before running the effect again. This is because useEffect is called on every render of the component and cleaning up before each new render will ensure no bugs get introduced. React is essentially running a new "version" of your effect on each render / update.
Conclusion
I will be experimenting with more React hooks in the future and will be sure to blog about them. I definitely recommend trying out useEffect for one of your fetch requests or other side effects. Thank you for reading!
Top comments (0)