React - useState, useEffect and component lifecycle
Index
useState hook
We need a place to put data that can change in our application, and we need to let React know when that state changes so it can update (or re-render) our app for us.
In React, state is associated to components and when the state changes, the component is updated. To get access to this state and to update it, we use what is called the useState
React Hook.
const [state, setState] = useState(initialState);
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState). During subsequent re-renders, the first value returned by useState will always be the most recent state after applying updates.
Example:
function Greeting() {
const [name, setName] = React.useState('')
const handleChange = event => setName(event.target.value)
return (
<div>
<form>
<label htmlFor="name">Name: </label>
<input onChange={handleChange} id="name" />
</form>
{name ? <strong>Hello {name}</strong> : "Please type your name"}
</div>
)
}
We pass it the default value of an empty string and react.useState returns an array with a stateful value, and a function to update it.
Lazy initializer with useState
It’s important to remember that the initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.
If the initial state is the result of an expensive computation, you can provide a function as the initial value. That function will be called to retrieve the initial value. That function will only be called when it’s absolutely necessary to retrieve the initial value.
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
It’s important to note that this function is called synchronously. It’s expected to be synchronous.
Example:
function Greeting() {
const [name, setName] = React.useState(
window.localStorage.getItem('name') || '',
)
console.log('rendered')
React.useEffect(() => {
window.localStorage.setItem('name', name)
})
...
}
useEffect hook
The function passed to useEffect
will run after every render.
The hook receives two arguments:
- the function to execute
- an array with values that will be evaluated after each render, these values are called dependencies.
Depending on the second argument you have 3 options with different behavior. The logic for each option is:
- If not present the effect will run after every render.
- With an empty array ([]) the effect runs only once, after
mounting
and the first render - An array with values [a, b, c] makes the effect evaluate the dependencies, whenever a dependency changes the effect will run
// Option 1 - no dependencies
useEffect(() => {
// heavy logic that runs after each render
});
// Option 2 - empty dependencies
useEffect(() => {
// create an event listener, subscription, fetch one-time data
}, []);
// Option 3 - with dependencies
useEffect(() => {
// fetch data whenever A, B or C changes.
}, [a, b, c]);
To sum-up the array options in the useEffect
:
Cleaning up effect
useEffects
creates resources that need to be cleaned up before the component leaves the screen. To do this, the function passed to useEffect
may return a clean-up function. For example, to create a subscription:
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});
The clean-up function runs before the component is removed from the UI to prevent memory leaks. Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect.
React component lifecycle
The chart that sum up the react component lifecycle:
from https://github.com/donavon/hook-flow