skip to Main Content

I have a todo list. I moved the todolist use memo, so that it updates only when todo is added, but ui does not seem to reflect

import "./styles.css";
import React from "react";

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <TodoList />
    </div>
  );
}

const Todo = ({ taskName }) => {
  console.log("in here todo");
  return <div>{taskName}</div>;
};

const TodoList = () => {
  const [todoList, setTodoList] = React.useState([]);
  const [iptValue, setIptValue] = React.useState("");
  const todoWrapper = React.useMemo(() => {
    return (
      <>
        {(todoList || []).map((item) => (
          <Todo taskName={item} key={item} />
        ))}
      </>
    );
  }, [todoList]);
  return (
    <div>
      {todoWrapper}
      <input
        type="text"
        onChange={(ev) => {
          setIptValue(ev.target.value);
        }}
      />
      <button
        onClick={() => {
          const todoArr = todoList;
          todoArr.push(iptValue);
          setIptValue("");
          setTodoList(todoArr);
        }}
      >
        Add Notes
      </button>
    </div>
  );
};

codepen link: https://codesandbox.io/s/stoic-cloud-phuxsk?file=/src/App.js:0-1082

2

Answers


  1. React’s useMemo does a shallow comparison via Object.is

    According to React’s documentation for useMemo,

    React will compare each dependency with its previous value using the Object.is comparison.

    And according to the MDN Web Docs for Object.is, it determines whether its arguments are the same. One of the rules is that if "both the same object (meaning both values reference the same object in memory)" then the object is the same.

    In your code you are calling setTodoList(todoArr) and todoArr is a reference to todoList. The only change you make to todoArr is the call to todoArr.push. This does not make a new reference in memory though. This simply modifies todoList and todoArr because they are one in the same.

    Create a new array

    What you need to do is create a new array and then call setTodoList. You can also use a library like Immer or Immutable.js to allow you to "modify" arrays and objects and get back a new copy instead.

    // Native react solution
    <button
      onClick={() => {
        const todoArr = [...todoList]; // Makes a new copy of the array
        todoArr.push(iptValue);
        setIptValue("");
        setTodoList(todoArr);
      }}
    >
      Add Notes
    </button>
    
    Login or Signup to reply.
  2. It may be unrelated but I just notice that the update pattern is incorrect either, when a new state is based/related with it’s original value, it should be set via a callback function:

    // onClick
    () => {
        setIptValue("");
        setTodoList((todoList) => {
            const todoArr = [...todoList];
            todoArr.push(iptValue);
            return todoList
        })
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search