简体   繁体   中英

LocalStorage doesn't display the data after submit and state doesn't persist on refresh

I'm writing a simple todo app and trying to save the data in local-storage. I've watched several tutorials considering using localStorage in React and did as they said step by step, and the code seems to be just fine. What seems to be the problem, is the local-storage itself. It should update automatically and display data when I add a todo item using localStorage.setItem(), but it doesn't. Only when I refresh it (the little refresh button just over the field with key-value pairs in the console in Application -> Local Storage) I can see the key-value pair displayed, so it looks like it has worked. However, even though it seems to keep the data after refreshing it manually, when I refresh the whole page, the data disappear. Below I included most of my code, perhaps I'm missing something and someone will notice the bug. Could someone help?

const [todos, setTodos] = useState([]);

  const addTodo = (e) => {
    e.preventDefault();
    if (inputValue.trim() === "") return;
    setTodos([
      ...todos,
      {
        text: inputValue,
        id: uuidv4(),
      },
    ]);

    setInputValue("");
  };

  useEffect(() => {
    const localData = localStorage.getItem("TODO_APP");
    if (localData !== null) setTodos(JSON.parse(localData));
  }, []);

  useEffect(() => {
    localStorage.setItem("TODO_APP", JSON.stringify(todos));
  }, [todos]);

Looks like useEffect conflict. Move the initialization into the useState call.

const [todos, setTodos] = useState(() => {
  const localData = localStorage.getItem("TODO_APP");
  if (localData !== null) return JSON.parse(localData);
  return [];
});

const addTodo = (e) => {
  e.preventDefault();
  if (inputValue.trim() === "") return;
  setTodos([
    ...todos,
    {
      text: inputValue,
      id: uuidv4(),
    },
  ]);

  setInputValue("");
};

useEffect(() => {
  localStorage.setItem("TODO_APP", JSON.stringify(todos));
}, [todos]);

I will add another slightly modified version. Should work:

const [todos, setTodos] = useState(JSON.parse(localStorage.getItem('TODO_APP')) || []);

const addTodo = (e) => {
    e.preventDefault();
    
    if (inputValue.trim() !== '') {
        setTodos([
            ...todos,
            {
                text: inputValue,
                id: uuidv4()
            }
        ]);
    }

    setInputValue('');
};

useEffect(() => {
    localStorage.setItem('TODO_APP', JSON.stringify(todos));
}, [todos]);

Other solutions might work for you, but if you are using React 18 , your code is perfectly fine, the issue was the useEffects called twice on refresh which was resetting localStorage. This is a kind of known issue.

You just need to disable StrictMode in the src/index.js file

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

reportWebVitals();

I tried to complete the missing part of the component and the final component looks like this:

import { useState, useEffect } from "react";

const Todo = () => {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState("");

  const onChangeHandler = (e) => {
    setInputValue(e.target.value);
  };

  const addTodo = (e) => {
    e.preventDefault();
    if (inputValue.trim() === "") return;
    setTodos([...todos, { text: inputValue }]);

    setInputValue("");
  };

  useEffect(() => {
    console.log("1st");
    const localData = localStorage.getItem("TODO_APP");
    console.log(localData);
    if (localData.length !== 0) setTodos(JSON.parse(localData));
  }, []);

  useEffect(() => {
    console.log("2nd");
    localStorage.setItem("TODO_APP", JSON.stringify(todos));
  }, [todos]);

  return (
    <form onSubmit={addTodo}>
      <input type="text" value={inputValue} onChange={onChangeHandler}></input>
      <button type="submit">ADD TODO</button>
    </form>
  );
};

export default Todo;

I added console.log() in the code sso you can observe how these useEffects are called on refresh

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM