Module 10: `useEffect` & Saving to LocalStorage

Congratulations on making it to the final module! Let's add one last feature: saving our todos so they persist even after the browser is refreshed.

1. The Problem: State is Not Persistent 💨

As you've noticed, if you add or delete todos in our app and then refresh the page, all your changes are lost. This is because component state only lives in the browser's memory and is reset every time the page loads.

To solve this, we need a way to save our `todos` state outside of the component. A perfect place for this is the browser's **LocalStorage**, a simple key-value storage system that persists until the user clears their browser data.

2. Side Effects and the `useEffect` Hook

Interacting with something outside of React (like LocalStorage, a network API, or the document title) is called a **"side effect"**. Performing side effects directly inside the main body of a component can be unpredictable.

The `useEffect` hook is the correct place to handle side effects in React. It's a function that runs *after* React has rendered your component to the DOM.

How `useEffect` works:

The `useEffect` hook takes two arguments:

  1. A **function** to run. This is your side effect code.
  2. A **dependency array**. This array tells React *when* to re-run your effect.

useEffect(() => {
  // This code runs when the component mounts
  // AND when any of the dependencies change
  console.log('The "todos" state has changed!');

}, [todos]); // The dependency array

If you provide `[todos]` as the dependency array, the effect function will run once when the component first loads, and then again **every time the `todos` state variable changes**.

3. Implementing Persistence in our Todo App 💾

We'll now modify our `TodoApp` from Module 9 with two changes.

Step 1: Load initial state from LocalStorage

First, we'll change our `useState` call to check if there are any todos already saved in LocalStorage. If so, we'll use that as the initial state.


const [todos, setTodos] = useState(() => {
  // Get the saved todos from localStorage
  const savedTodos = localStorage.getItem('todos');
  
  // If there are saved todos, parse them back into an array
  if (savedTodos) {
    return JSON.parse(savedTodos);
  } else {
    // Otherwise, return a default empty array
    return [];
  }
});
Step 2: Save state to LocalStorage whenever it changes

Next, we'll add a `useEffect` hook that watches our `todos` state. Whenever `todos` changes, this effect will run and save the new array to LocalStorage.


const { useState, useEffect } = React;

// (Inside the TodoApp component)
useEffect(() => {
  // Convert the todos array to a JSON string and save it
  localStorage.setItem('todos', JSON.stringify(todos));

}, [todos]); // This effect depends on the 'todos' state

And that's it! With these two changes, our application now has persistent state. You can add and remove todos, refresh the page, and your list will be exactly as you left it.