React useEffect
up:: React Hooks
What are side effects
- React’s main job: Render the UI & React to User Input.
- Side Effects: Anything else. React is not primarily concerned with this.
Side Effects should not go into component functions.
If we want to fetch some data, that can alter our state. This leads to the component function being run, fetching data again and creating an infinite loop.
Anything not related to rendering components should not go into component functions.
Examples “Code that should be executed in response to something”
- http requests
- keystrokes
side effect go into useEffect() hook
useEffect(
() => {}, // run this function *after* component evaluation
[ dependencies ] // if these dependecies changed
);
Dependencies
When you pass in an empty array, the useEffect hook only runs once. No dependencies were passed in to check, so it doesn’t change.
Always add all values into dependencies
Otherwise bad things happen.
example without dependencies
// state defined, locaStorage set somewhere else
useEffect(() => {
const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
if (storedUserLoggedInInformation === "1") {
setIsLoggedIn(true);
}
}, []);
dependencies
useEffect(() => {
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
setFormIsValid(
enteredPassword.trim().length > 6 && enteredEmail.includes("@")
);
}, [setFormIsValid, enteredEmail, enteredPassword]);
This will evaluate the function if setFormIsValid
or enteredEmail
or enteredPassword
changed. You can actually omit setFormIsValid
. It’s a state function, so always stays the same since it is React built-in.
But enteredEmail
and enteredPassword
change on every keystroke (This isn’t reflected in the above code but is the case in the example project).
Cleanup function
Lingo: Debouncing
Say you have a log in component. You want the button to be active when email and password are valid.
Now, you don’t want to check the validity on every keystrokes. That’s a lot of network requests. Instead, you can wait for the user to stop typing for, say, 500ms. This technique is called debouncing.
You can return
something in a useEffect()
function. This is called a cleanup function. It runs before each time useEffect runs again.
Example So we could have a useEffect function that enables a button if the password is valid. In that function, we add a timeout. Check the password after 500ms.
Problem: This simply creates a dozen of timeouts. The first one executes after 500ms no matter if we stopped typing.
So we need to clear the interval after the first evaluation. Add it into the cleanup function and we’re good to go.
Summary
import React, { useEffect } from 'react';
// => Runs everytime the component is updated in any way. You rarely want thisuseEffect(() => {
console.log("EFFECT RUNNING");
})
// ----------------
// => Runs once when the component is 'mounted' (loaded into the DOM)
useEffect(() => {
console.log("EFFECT RUNNING");
}, [])
// ----------------
// => Runs once when the component is 'mounted' and anytime enteredEmail changes
useEffect(() => {
console.log("EFFECT RUNNING");
}, [enteredEmail])
// ----------------
// => The cleanup function runs after anytime the function runs
useEffect(() => {
console.log("EFFECT RUNNING");
return () => {
console.log("CLEANUP")
}
}, [enteredEmail])