If you are new to React or you used React with class based components, you are probably trying to understand how exactly useEffect
works, when to use and how to use it.
But before we show any code, we need to understand that a React component has a few various lifecycle events, and the main ones are the following:
- A component is created
- A component, or it's state, changes
- A component is destroyed
And React has some very hand hooks that you can use to 'hook' to those events (sorry for the pun).
The useEffect
hook
Now before we see how the hook works in practice, we need to see how this function is expecting from us, the developer, to be called.
The useEffect(callback, [dependencies])
expects a callback function as a first argument, and this must not be an async function, to essentially define what to execute when the hook is invoked.
The second argument, that can be optional, represents the dependencies, and the dependencies are essentially a list of state variables that we want to listen to, but I'll explain later in more detail.
But let's see how it works.
A component is created
When a component is added to the virtual DOM, and it's rendered for the first time, this is when we can say it has been created.
If you used React in the past, to handle compoment events, you probably used something like this code:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
};
}
// component is mounted
componentDidMount() {
// do something
this.setState({
name: 'Fred'
});
}
// component state changed
componentDidUpdate(prevProps, prevState) {
//do something
}
// component is destroyed
componentDidUnmount() {
// do something
}
render() {
const { name } = this.state;
return <div>My name is {name}</div>;
}
}
So in the component above, we'll have our state
to contain a variable called name
that is empty at the very beginning, but right after the component is mounted, or created, will be set to Fred
.
To do the exact same behaviour as above with the useEffect
hook, using a functional component, our code will look like this:
import { useEffect, useState } from 'react';
const MyComponent = () => {
const [name, setName] = useState('');
useEffect(() => {
setName('Fred');
}, []);
return <div>My name is {name}</div>;
}
But let's explain this code a little bit.
As you see, we tell the useEffect
to invoke a function that updates the state for the name
state variable, but as a dependencies we pass an empty array.
With this hook, an empty array simply means "do it when the component mounts and only once".
So you might ask, why the need to pass an empty array? Why we don't simply pass nothing?
Because with the useEffect
hook, if you pass no dependencies at all, it will call the callback function on every single render of the component, not only at the beginning, and that's something you normally want to avoid to to have the event handler to unnecessarily get detached and reattached on every time the component renders.
A component is updated
If you want to listen to a state variable and see when it changes, here is the place where you want to pass the array of dependencies.
For example in this code we have a firstname
and lastname
variables, and we want to console.log
the variable every time it changes, our code will look like this:
import { useEffect, useState } from 'react';
const MyComponent = () => {
const [firstname, setFirstname] = useState('Fred');
const [lastname, setLastname] = useState('Flinstone');
useEffect(() => {
console.log(firstname)
}, [firstname]);
const handleFirstname = newName => setFirstname(newName);
const handleSurname = newName => setLastname(newName);
return (
<div>
<p>My name is {firstname} {lastname}</p>
<a onClick={() => handleFirstname('Barney')}>Change firstname</a>
<a onClick={() => handleSurname('Rubble')}>Change lastname</a>
</div>
);
}
In the code above we set the initial firstname to be "Fred", but when we click on the component it changes to "Steve", and our hook will listen to the firstname
to change, running the callback only when that variable changes, and not when any other ones do, like the lastname
one.
And you can also use multiple useEffect
in your component, for example if we want one hook each for our variables, we can something like this:
useEffect(() => {
// do something when firstname changes
}, [firstname]);
useEffect(() => {
// do something when lastname changes
}, [lastname]);
A component is destroyed
The last lifecycle event I want to show you is the one invoked when a component is destroyed, or removed by the virtual DOM.
This is rather simple, and essentially all you need to do, is to return a function inside the callback of the useEffect
.
Let's say that you want to do some stuff when you create the component, like reading some data from a datasource, and when you destroy the component you just want to get rid of it, your hook will look like this:
import { useEffect, useState } from 'react';
const MyComponent = () => {
const [data, setData] = useState({});
useEffect(() => {
// fetch some data from somewhere and add it to the state
setData(someStuff);
return () => {
// just empty the data
setData({});
}
}, []);
}
And again, this can be done with dependencies as well if you need to.
The above lifecycle event is not used extremely often, but it can be handy in some occasions, like telling a websocket connection to disconnect for example.
I hope that this post was useful for you to better understand how to use the React useEffect hook.
Top comments (0)