useState and useEffect explained
They are fun and concise
UPDATE : I have made a video on this topic you can watch it here https://youtu.be/ek-62uxioNE
React is a great library to write reusable UI. The problem React faced for a long time is reusing logic. Developers introduced techniques like HOC and Render Props to overcome this, but both of the techniques easily go off the rails and components become very complex ( hard to reason about ) if we use these techniques heavily.
React introduced Hooks to overcome this obstacle. React Hooks are a great way to reuse logic throughout your project. Not only they provide you a way to reuse logic in my opinion, but they also help in cleaning the syntax by making your code look more like simple functions.
The most popular and commonly used hooks are useState
and useEffect
useState
The state can now be used inside functional components that is made possible by the useState
hook. Let’s have a look at how we can use useState
let [item, setItem] = useState('default value')
Let’s break things down.
we call the useState
function and pass a default value as an argument. The useState
function returns an array which contains two items item
and setItem
The item
is the variable which will store our value and setItem
is an updater function which will be responsible to update the item
simple as that.
We should also consider the default value
passed to useState
will be the initial value of the item
. In our case, it’s a string but it can be of any datatype.
For updating the value in the state you just have to do something like this.
<button onClick={() => setItem('Macbook Pro')}>
Add Macbook Pro
</button>
You should also note that setItem
function always overwrites the value rather than merging the new value to old value. For e.g.
let [person, updatePerson] = useState({ name: 'Manoj', age: 21 })return (
<button onClick={() => updatePerson({ age: 22 })}>Update age</button>
)// in the next render the person will be
{ age: 22 }
// rather than
{ name: 'Manoj', age: 21 }
For solving this useState
also provide a very simple solution. We can also pass a function rather than a value to updatePerson
and in that function, our previous state will be available.
<button onClick={prevState => updatePerson({ ...prevState, age: 22 })}>Update age</button>
Once we have the previous state we can merge the new and previous state to avoid overwriting the previous state with the new one.
useEffect
useEffect
is an interesting one. It removes the need for componentDidMount
, componentDidUpdate
and componentWillUnmount
because it handles the use case of all of these life cycle methods. Let’s have a look
useEffect(() => {
// hit an API
}, [])
This useEffect
will work like a componentDidMount
Let’s break this down.
useEffect
takes a function which can contain any kind of operation including side effects. Any kind of side effect is not allowed inside the render method (or inside the function in case of a functional component). So any kind of side effect should be used inside useEffect
useEffect
also takes a second argument as an array []
, in this array you can pass variables. When any of this variable updates it will cause the useEffect
to run again, because we passed an empty array our useEffect
will only run once throughout the life of our component.
If we didn’t pass the second argument something like this
useEffect(() => {
// hit an API
})
This will cause useEffect
to run after every render, just like componentDidUpdate
but I doubt you would be using this a lot because most of the time we don’t want the side effects to run after every render.
Another use case for useEffect
will be something like this
let [item, setItem] = useState('default value')useEffect(() => {
// hit an API
}, [item])return (
<button onClick={() => setItem('Macbook Pro')}>
Add Macbook Pro
</button>
)
Alright, so what is happening here?
We have a variable name item
in our state. When we click the Add Macbook Pro
our item
value will be updated to Macbook Pro
and also our useEffect
will get triggered because we passed the item
to useEffect
and because of that whenever we update the value of item
our useEffect
will run.
Last but not least let’s have a look at how we should implement componentWillUnmount
using useEffect
Most of the use case of componentWillUnmount
is for cleaning of event listeners
or subscriptions
( like socket.io events) let’s see how we can implement that.
useEffect(() => {
window.addEventListener('click', () => console.log('capture clicks'))
return () => {
window.removeEventListener('click', () => console.log('clean clicks listener'))
}
}, [])
Whatever function we return from the useEffect
will be treated as componentWillUnmount
and will run either when the useEffect
runs again or when the component is about to leave the UI.
By combining all of the life cycles methods into one hook we can easily group the same kind of functionality together rather than only one useEffect
we can also use as many useEffect
we want to add. That means you can subscribe to an event and unsubscribe from it inside one useEffect
and hit APIs in another useEffect
The order of hooks
A very important thing we have to understand is that we can’t change the order or numbers of the hooks in each render. It must be the same in every render. That means we can’t use conditional hooks. For e.g
if (item) {
useEffect(() => {}) // you can't do this
}or this items.forEach(() => useEffect(() => {}))// because if the length of items changes the useEffect count will not be same in the next render.
keep sharing 😃 and clapping 👏
Here are more articles for you.
Hi, My name is Manoj Singh Negi. I am a Javascript Developer and writer follow me at Twitter or Medium.
I am available to give a public talk or for a meetup hit me up at justanothermanoj@gmail.com if you want to meet me.
Really loved this article ?
Please subscribe to my blog. You will receive articles like this one directly in your Inbox frequently.
Peace.