The Why?
React components are functions, you provide them with input (props) and expect a predictable output.
const Profile = ({ name, age }) => {
return (
<div>
<span>name: {name}</span>
<span>age: {age}</span>
</div>
);
};
Looking at the code above, if you used it like below, you should expect the Profile component to display an output of name: “John” and age: 24.
const App = () => {
return <Profile name="John" age={24} />
}
The above is called “Pure function”, in short, pure functions are predictable functions, given the same input they will always return the same output.
The second rule of a pure function is: it has no side effects.
A few examples of side effects are:
- fetch and web APIs (setTimeout, setInterval, …etc)
- DOM mutation
And here comes the why…useEffect allows you to run side effects in your React component without blocking the component flow.
What I mean by component flow, is that the effects don’t interfere with the component rendering. useEffect runs after the component has been rendered, thus it is non-blocking.
To sum up, useEffect allows you to write non-blocking side effects in your component.
Why is it important? Because it allows your app to have an asynchronous beauty to it. Would you rather have your user blocked out of using the application while waiting for the data to be fetched? or would you prefer them to have the request run in the background while the app is responsive and interactive in the foreground?
The How?
Before React hooks came to light, we had 3 bad boys:
componentDidMount
componentDidUpdate
componentWillUnmount
ComponentDidMount would run after the component's initial render.
ComponentDidUpdate would run after each component’s re-render.
ComponentWillUnmount would run just before the component is about to be removed.
They ought to be used depending on the “timing”.
You need to run something only once the component is rendered and that’s it? Maybe a fetch request, then componentDidMount
would have been your choice.
If you wish to run something when to run once and every time a prop is changed, then componentDidUpdate
would do the job.
The component is about to be removed from the dom? then componentWillUnmount
would be the resort.
Now, things are pretty much easier. Or so I think.
React combined those 3 bad boys into one big bad boy and named himuseEffect
Let’s see how we could use useEffect
to have the same advantage that was given to us by the 3 bad boys.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
ComponentDidMount in React hooks
To replicate componentDidMount
or in other words, have a side effect run only once as soon as the component is rendered. We would write:
useEffect(() =>{
// code goes here...
}, [])
Your eyes caught that we pass two arguments, first a function that has the effects you want to execute, second an array.
The array is called a “dependency array”, in simple words, run this code when any dependency in the array changes. Since it is empty, it will only run once.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
ComponentDidUpdate in React hooks
If you paid enough attention to the previous example, you would know that all you would have to do is to provide a prop/state/variable to the dependency array change to configure useEffect re-run whenever a dependency changes.
const [state, setState] = useState(...)
useEffect(() => {
// code goes here...
}, [state])
Now, whenever state
changes this useEffect will execute.
Important note: the code above will run on initial render and every time
state
changes. Thus, it is a combination ofcomponentDidMount
andcomponentDidUpdate
Buuuuuut…What if I want to run an effect everytime the component re-renders and not a specific state/prop? Like all the time!
Simple, don’t provide a dependency array!
useEffect(() => {
// code goes here...
})
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
ComponentWillUnmount in React hooks
This one is mainly used for cleanup, you know stuff like clearTimeout, cancel fetch request, unsubscribe…etc.
useEffect(() => {
const timer = setTimeout(() => {}, 1000);
return () => {
// rest of cleanup goes here...
clearTimeout(timer)
}
}, [])
It is simple, just return a function. The cleanup function will only run when a component is about to be removed.
I could write further, but I am in short in time.
Thank you for reading.