DEV Community 👩‍💻👨‍💻

Cover image for Don't Update State Directly In React
Shaban-Eissa
Shaban-Eissa

Posted on

Don't Update State Directly In React

In this post, we will discuss how to update state in correct way

first, App.js that contain list of todos and pass them as props to Todos component, after this we receive props in Todos component and passing them as props to Todo component to render every todo in list of todos, in Todos.js there is handleToggleComplete to make checkbox marked or not and contain handleSelect to detect which todo is selected

App.js code :

import Todos from "./Todos";

export default function App() {

  const initialTodos = [
    {
      id : 1 ,
      complete : false ,
      name : 'first todo'
    } , 
    {
      id : 2 ,
      complete : true ,
      name : 'second todo'
    } ,
    {
      id : 3 ,
      complete : false ,
      name : 'third todo'
    }
  ]
  return (
    <div className="App">
     <Todos initialTodos = {initialTodos} />  
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Todos.js code :

import React, { useState } from "react";
import Todo from "./Todo";

const Todos = ({ initialTodos }) => {
  const [todos, setTodos] = useState(initialTodos);
  const [selectedTodo, setSelectedTodo] = useState();

  const handleToggleComplete = (todoId) => {
    setTodos((currTodos) => {
      const todo = currTodos.find((todo) => todo.id === todoId);
      todo.complete = !todo.complete;
      return currTodos;
    });
  };

  const handleSelect = (todoId) => {
    setSelectedTodo(todos.find((todo) => todo.id === todoId));
  };

  return (
    <>
      {todos.map((todo) => (
        <Todo
          key={todo.id}
          todo={todo}
          handleToggleComplete={handleToggleComplete}
          handleSelect={handleSelect}
        />
      ))}
      <h3>Selected Todo</h3>

      {selectedTodo && (
        <Todo
          todo={selectedTodo}
          handleToggleComplete={handleToggleComplete}
          handleSelect={handleSelect}
        />
      )}
    </>
  );
};

export default Todos;

Enter fullscreen mode Exit fullscreen mode

Todo.js code :

import React from "react";

const Todo = ({ todo, handleToggleComplete, handleSelect }) => {
  const toggleComplete = () => {
    handleToggleComplete(todo.id);
  };

  const onSelect = () => {
    handleSelect(todo.id);
  };
  return (
    <div>
      <input
        type="checkbox"
        checked={todo.complete}
        onChange={toggleComplete}
      />
      {todo.name}
      <button onClick={onSelect}>Select</button>
    </div>
  );
};

export default Todo;

Enter fullscreen mode Exit fullscreen mode

now let's see the result of this code

Image description

as we see we can't update any checkbox, this because we update state directly, in setTodos we update currTodos directly and return currTodos

 const handleToggleComplete = (todoId) => {
    setTodos((currTodos) => {
      const todo = currTodos.find((todo) => todo.id === todoId);
      todo.complete = !todo.complete;
      return currTodos;
    });
  };
Enter fullscreen mode Exit fullscreen mode

to solve this problem we can update state by take copy of currTodos and update through copied state

const handleToggleComplete = (todoId) => {
    setTodos((currTodos) => {
      const copy = [...currTodos]
      return copy.map(todo => {
        if(todo.id === todoId) {
          return {...todo  , complete : !todo.complete}
        }
        return todo 
      })
    });
  };

Enter fullscreen mode Exit fullscreen mode

Image description

to make more clarification about how to update state in code above,
1- copy is array we make update through it
2- if there is update return {...todo , complete : !todo.complete} this will be updated in copy array
3- if there is no update return todo
4- all this array (copy array) will be returned as new state as we say setTodos(copy)

 return copy.map(todo => {
        if(todo.id === todoId) {
          return {...todo  , complete : !todo.complete}
        }
        return todo 
      })
Enter fullscreen mode Exit fullscreen mode

let's see the result now :

Image description

now we can update state of each checkbox successfully, but let's see what about the selectedTodo :

Image description

as we see, we selected third todo, but when mark or unmark checkbox from third todo it didn't update on selectedTodo that is becuase we stored the derived state of selectedTodo

in this code below the wrong is we store derived state, so keep in mind don't store derived state
setSelectedTodo(todos.find((todo) => todo.id === todoId))

the correct way is using this line of code below so every re-render we calculate updated selectedTodo and we can using useMemo for this
const selectedTodo = todos.find(todo => todo.id === selectedTodoId)

and that is the code below in Todos.js with all changes

import React, { useState } from "react";
import Todo from "./Todo";

const Todos = ({ initialTodos }) => {
  const [todos, setTodos] = useState(initialTodos);
  const [selectedTodoId, setSelectedTodoId] = useState();

  const selectedTodo = todos.find(todo => todo.id === selectedTodoId)

  const handleToggleComplete = (todoId) => {
    setTodos((currTodos) => {
      const copy = [...currTodos]
      return copy.map(todo => {
        if(todo.id === todoId) {
          return {...todo  , complete : !todo.complete}
        }
        return todo 
      })
    });
  };

  return (
    <>
      {todos.map((todo) => (
        <Todo
          key={todo.id}
          todo={todo}
          handleToggleComplete={handleToggleComplete}
          handleSelect={setSelectedTodoId}
        />
      ))}
      <h3>Selected Todo</h3>

      {selectedTodo && (
        <Todo
          todo={selectedTodo}
          handleToggleComplete={handleToggleComplete}
          handleSelect={setSelectedTodoId}
        />
      )}
    </>
  );
};

export default Todos;

Enter fullscreen mode Exit fullscreen mode

let's see the result now of this code :

Image description

That is all about how to update state in correct way, keep in mind always not update state directly, take copy of state and update through the copied state

Image description
Thanks for your time to read this post

Top comments (0)

🌚 Life is too short to browse without dark mode