Home > Software engineering >  react don't re-render when context value change
react don't re-render when context value change

Time:11-23

react doesn't re-render when context value change

I use tailwindcss, React(v18.2.0) and vite(3.2.4). I have tried

i wan't when I click in TodoItem , i change completed value in the todo with react context

App.jsx

import { useState } from 'react'
import InputTodo from './components/InputTodo'
import Todos from './components/Todos'
import TodosContext from './contexts/todos'

function App() {
  const [todos, setTodos] = useState(null);
  const value = { todos, setTodos }

  return (
    <TodosContext.Provider value={value}>
      <div className="App flex flex-col items-center w-screen h-screen">
        <InputTodo></InputTodo>
        <Todos></Todos>
      </div>
    </TodosContext.Provider>
  )
}

export default App

TodoContext.js

import { createContext } from "react";

const TodosContext = createContext(null)

export default TodosContext;

InputTodo.jsx(component add todo in context value)

import { useContext, useState } from 'react'
import TodosContext from '../contexts/todos';
import { v4 as uuid} from "uuid"

function InputTodo() {
  const [inputValue, setInputValue] = useState("");
  const { todos, setTodos } = useContext(TodosContext);

  const handleSubmit = (ev) => {
    ev.preventDefault();
    setTodos((current) => {
      if (todos) return [...todos, { id: uuid(), name: inputValue, completed: false }]
      else {
        return [{ id: uuid(), name: inputValue, completed: false }]
      }
    })
    setInputValue("")
  }

  return (
    <form className="App flex m-8" onSubmit={handleSubmit}>
      <input className='border-sm border-2 border-black text-center focus:outline-none rounded-lg' value={inputValue} placeholder='entrer votre todo' onChange={(ev) => setInputValue(ev.target.value)}></input>
      <button type='submit' className='flex justify-center items-center mx-4 border-2 border-black rounded-lg'>envoyer</button>
    </form>
  )
}

export default InputTodo

Todos.jsx(components will map all todo)

import { useContext } from "react";
import TodosContext from "../contexts/todos";
import TodoItem from "./TodoItem";

function Todos() {
    const { todos, setTodos } = useContext(TodosContext)
    return (
        <div className="w-10/12 h-5/6 overflow-visible border border-black">{!todos ? <h1 className="text-center text-3xl">aucune todo</h1> : todos.map((todo) => <TodoItem key={todo.id} name={todo.name} id={todo.id} completed={todo.completed}></TodoItem>)}</div>
    )
}

export default Todos;

TodoItem.jsx(components will rendered in Todos.jsx)

import { useContext } from "react";
import TodosContext from "../contexts/todos";

function TodoItem({ id, name, completed }) {
    const { todos, setTodos } = useContext(TodosContext)
    return (
        <li className="w-full h-12 flex items-center justify-center" onClick={() => {
            setTodos((prevState)=>{
                const index = todos.findIndex((todoItem) => todoItem.id === id)
                const newState = todos
                newState[index].completed = true
                return newState;
            })
        }}><div className={`${completed ? "line-through" : ""}`}>{name}</div></li>
    )
}

export default TodoItem;

CodePudding user response:

You are mutating your state, which in turn hides the update from react.

Try this:

setTodos((prevState) => {
   const index = prevState.findIndex((todoItem) => todoItem.id === id)
   const newState = [...prevState]
   newState[index] = {...prevState[index], completed: true}
   return newState
})
  • Related