React lifecycle and Rendering
Without useEffect
Use Case: When some information needs to be updated on every render or comes from a hook other than useEffect since hooks must be within react components rather than nested in each other. The page will render every time a value changes causing these values to be updated. You cant use this like state because it does not know when the data has changed without useState.
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Functions passed to useState, useMemo, or useReducer
This means the initial useEffect calls will occur twice if you are using Strict mode.
const Component = () => {
const number = 5;
console.log("rerendered");
return (<div>{number}</div>);
}
Mount
Use Case: If you need something to only happen once after the first render when it is mounted. The only way this will be triggered again is if the page reloads. Changing query params on the same page will not trigger it it mount becuase it still exists on the page.
If a component came to exist such as appearing on a page it has mounted.
Example:
useEffect(() => {
//code to happen on first render
}, [])
Update
When we update a dependency the callback is put on the stack of things to be updated.
Use Case: When you want to control when a set of code runs based on changes to state.
Example:
useEffect(() => {
//Runs every time the state changes
}, [state])
Unmount
A component unmounts when it no longer appears on the screen.
Use Case: If we are using useEffect to fetch from an API then we should use this return statement to cleanup in the case that the component unexpectedly unmounts before the request is completed.
The error will look something like this:
Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
We can use a flag or a AbortController. If we do not need to support IE AbortController is a better option because the preflight request would remain in the background consuming user bandwidth and taking up one of the browser’s max concurrent requests.
Example:
useEffect(() => {
return () => {
//Runs when component unmounts.
}
}, [])
useEffect(() => {
let abortController = new AbortController();
const fetchData = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1', {
signal: abortController.signal,
});
const newData = await response.json();
setTodo(newData);
}
catch(error) {
if (error.name === 'AbortError') {
// Handling error thrown by aborting request
}
}
};
fetchData();
return () => {
abortController.abort();
}
}, []);
Components in components
In Formik you are required to place useEffect in its own component in order to access formiks data. In a case like this make sure the component is not defined inside the initial component to avoid infinite rerenders as every time the parent component renders the child will also rerender which means a lot more mounting than you likely intended.
Why isnt this rerendering
If you are attempting to change state one of two things is happening:
- You are not setting state with useState
You must use the setter from useState. Trying to set this manually will cause unexpected issues.
- The reference is not changing
To fix this if you are using an array you can destructuring to change the reference. ie. […myState]
Using many useEffects
If there is a direct relationship between data we should avoid using more than one useEffect.
rerenders with context/redux
Passing down the store state will cause all the descendant nodes to re-render.
All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.
This is why one global state is not recommended and context API is better suited for state which is not changing frequently (theme, localization, auth…). If you use context API you should split the context.
References
How useEffect works in ReactJS ? — GeeksforGeeks
useEffect under the Hood (bussieck.com)
When does React re-render components? | Felix Gerschau
Avoiding race conditions and memory leaks in React useEffect — Wisdom Geek