Understanding React `useState` and `useEffect` hooks
React hooks are JavaScript functions that you can import from React package in order to add features to your components. Hooks are available only for function-based components, so they can't be used inside a class component.
React provides you with 10 hooks function, but only 2 of these hooks are going to be used very frequently when you write function components. They are useState
and useEffect
hooks. Let's learn about useState
first.
The useState
hook is a function that takes one argument, which is the initial state, and it returns two values: the current state and a function that can be used to update the state. Here's the hook in action:
import React, { useState } from 'react'
function UserComponent() {
const [name, setName] = useState('John')
}
Notice the use of square brackets when state variable is declared. This is the ES6 array destructuring syntax, and it means we’re assigning the first element of the array returned by useState to name
and the second element to setName
variable.
So this means we have a state named name
and we can update it by calling on setName()
function. Let’s use it on the return statement:
import React, { useState } from 'react'
function UserComponent() {
const [name, setName] = useState('John')
return <h1> Hello World! My name is {name} </h1>
}
Since function components don't have the setState()
function, you need to use the setName()
function to update it. Here's how you change the name from "John" to "Luke":
import React, { useState } from 'react'
function UserComponent() {
const [name, setName] = useState('John')
if(name === "John"){
setName("Luke")
}
return <h1> Hello World! My name is {name} </h1>
}
When you have multiple states, you can call the useState
hook as many times as you need. The hook receives all valid JavaScript data types such as string, number, boolean, array, and object:
import React, { useState } from 'react'
function UserComponent() {
const [name, setName] = useState('Jack')
const [age, setAge] = useState(10)
const [isLegal, setLegal] = useState(false)
const [friends, setFriends] = useState(["John", "Luke"])
return <h1> Hello World! My name is {name} </h1>
}
And that's all there is to it. The useState
hook basically enables function components to have its own internal state.
useEffect
hook
The The useEffect
hook is the combination of componentDidMount
, componentDidUpdate
and componentWillUnmount
class lifecycle methods. This hook is the ideal place to set up listeners, fetching data from API and removing listeners before the component is removed from the DOM.
Let’s look at an example of useEffect in comparison with class lifecycle methods. Normally in class component, we write this kind of code:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Nathan',
};
}
componentDidMount() {
console.log(
`didMount triggered: Hello I'm ${this.state.name}`
);
}
componentDidUpdate() {
console.log(
`didUpdate triggered: Hello I'm ${this.state.name}`
);
}
render() {
return (
<div>
<p>{`Hello I'm ${this.state.name}`}</p>
<button
onClick={() =>
this.setState({ name: 'Gary'})
}
>
Change me
</button>
</div>
);
}
}
Since componentDidMount
is run only once when the component is inserted into the DOM tree structure, subsequent render won’t trigger the method anymore. In order to do run something on each render, you need to use componentDidUpdate
method.
Using useEffect
hook is like having both componentDidMount
and componentDidUpdate
in one single method, since useEffect runs on every render. It accepts two arguments:
- (mandatory) A function to run on every render
- (optional) An array of state variables to watch for changes.
useEffect
will be skipped if none of the variables are updated.
Rewriting the above class into function component would look like this:
const Example = props => {
const [name, setName] = useState('Nathan');
useEffect(() => {
console.log(`Hello I'm ${name}`);
});
return (
<div>
<p>{`Hello I'm ${name}`}</p>
<button
onClick={() => {
setName('Gary')
}}>
Change me
</button>
</div>
)
}
The function component above will run the function inside of useEffect
function on each render. Now this isn’t optimal because the state won’t be updated after the first click. This is where useEffect
second argument come into play.
useEffect(() => {
console.log(`Hello I'm ${name} and I'm a ${role}`);
},
[name]);
The second argument of useEffect
function is referred to as the "dependency array". When the variable included inside the array didn't change, the function passed as the first argument won't be executed.
The componentWillUnmount effect
If you have code that needs to run when the component will be removed from the DOM tree, you need to specify a componentWillUnmount
effect by writing a return
statement into the first argument function. Here is an example:
useEffect(() => {
console.log(`useEffect function`);
return () => { console.log("componentWillUnmount effect"); }
}, [name] );
Running useEffect only once
To run useEffect
hook only once like componentDidMount
function, you can pass an empty array into the second argument:
useEffect(
() => {
console.log(`useEffect function`);
},
[] );
The empty array indicates that the effect doesn't have any dependencies to watch for change, and without a trigger, it won't be run after the component is mounted.
Level up your React skill today!
Get my latest book and build FOUR projects using React. You get 40% off with my Black Friday deal! Grap a copy today!