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])