skip to Main Content

I’m trying to add localStorage to my Todo on React, but I getting an error Cannot read properties of null (reading ‘concat’) when I try to add new todo item. How to fix it? Here my code:

import { useState, useEffect } from 'react';
import TodoInput from './TodoInput';
import TodoList from './TodoList';
import './App.css';

const App = () => {
  const [todo, setTodo] = useState('');
  const [todos, setTodos] = useState([]);
  const localStorage = window.localStorage;
  const [localTodos, setLocalTodos] = useState([]);

  useEffect(() => {
    setLocalTodos(JSON.parse(localStorage.getItem('todos')));
  }, []);

  function addTodo() {
    if (todo !== '') {
      setLocalTodos(JSON.parse(localStorage.getItem('todos')));
      localTodos = localTodos.concat([todo]);
      localStorage.setItem('todos', JSON.stringify(localTodos));
      setTodo('');
    }
  };

  function deleteTodo(text) {
    localTodos = JSON.parse(localStorage.getItem('todos'));

    localTodos = localTodos.filter((todo) => {
      return todo !== text;
    });

    setLocalTodos(localTodos);
  };

  return (
    <div className="App">
      <h1>Your todos</h1>
      <TodoInput todo={todo} setTodo={setTodo} addTodo={addTodo} />
      <TodoList list={localTodos} remove={deleteTodo} />
    </div>
  );
}

export default App;

2

Answers


    1. You cannot mutate the state this way:
    localTodos = localTodos.concat([todo]); // <-- wrong approach
    localStorage.setItem('todos', JSON.stringify(localTodos));
    

    You have to create a copy of the state so you can update this latter and then use it

    let copyLocalTodos = [...localTodos]
    copyLocalTodos = copyLocalTodos.concat([todo]);
    localStorage.setItem('todos', JSON.stringify(copyLocalTodos));
    

    You have to make those changes in both your deleteTodo and addTodo functions


    1. setState is asynchronous
    setLocalTodos(JSON.parse(localStorage.getItem('todos')));
    localTodos = localTodos.concat([todo]); // don't expect the state to be updated here cause `setState` is async
    

    Also you can initilize your state with the value from localstorage

    const [localTodos, setLocalTodos] = useState(JSON.parse(localStorage.getItem('todos')) || []);
    
    Login or Signup to reply.
  1. There’s a lot more to the problem than just that one error:

    1. You’re trying to manually over-write a state value, which is entirely wrong.
    2. You’re trying to read the updated state value before it’s actually been updated.

    Also, consider this approach logically. You just queued an update to that state value on the immediate previous line. If you want to update state to a concatenated value, first concatenate the value and then update state.

    For example:

    function addTodo() {
      if (todo !== '') {
        let newTodos = JSON.parse(localStorage.getItem('todos'));
        newTodos = newTodos.concat([todo]);
        setLocalTodos(newTodos);
        localStorage.setItem('todos', JSON.stringify(newTodos));
        setTodo('');
      }
    };
    

    Don’t forget to also update the call to setItem to use the newTodos value, since localTodos won’t have been updated yet at that time.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search