Изменить состояние существующего объекта - PullRequest
0 голосов
/ 05 марта 2020

Здравствуйте. Я работаю с контекстом реакции и столкнулся с проблемой.

У меня есть этот компонент, который содержит все логи c:

import React , {useContext} from 'react';
import {TodoContext} from '../../contexts/todo-context';
import Todo from './Todo'; 
import './Todos.css';

export default function TodoList() {
    const [todos , setTodos] = useContext(TodoContext);

    const markComplete = (e) => {
       setTodos(prevState => [...prevState , {...e.target.value , done:true} ]); 
       console.log(todos);
    }


    return (
        <div className="TodoContainer">
            {todos.map(todo => (<Todo key={todo.id} todo={todo} markComplete={markComplete} /> ))}
        </div>
    )
}

Я передаю функцию markComplete, которая будет использоваться в компоненте Todo.

Компонент Todo:

import React из 'response'

export default function Todo({todo , markComplete}) {
    return (
        <div>
            <input type="checkbox" value={todo} onChange={markComplete}></input>
            <li>{todo.title}</li>
            <div className="spacer"></div>
        </div>
    )
}

Здесь у меня есть флажок, который onChange должен запускать мою функцию markComplete. Это работает, но функция markComplete по какой-то причине не меняет моего состояния. Вот снова функция:

const markComplete = (e) => {
   setTodos(prevState => [...prevState , {...e.target.value , done:true} ]); 
   console.log(todos);
}

Она также генерирует ошибку о том, что я должен передать ключ (что я уже делаю)

Состояние:

const [todo, setTodo] = useState([
    {
        title : 'Do the dishes', 
        done : false , 
        id: 123 
    },
    {
        title : 'wash car', 
        done : false , 
        id: 423 
    },
    {
        title : 'Buy pen', 
        done : false , 
        id: 323 
    }
]); 

1 Ответ

2 голосов
/ 05 марта 2020

Неправильный способ обновления вашего состояния.

Обратите внимание, что в элементе todo вы сделали:

<input type="checkbox" value={todo} onChange={markComplete}></input>

Хотя todo само по себе является объект, что означает, что при этом:

setTodos(prevState => [...prevState , {...e.target.value , done:true} ]); 

Вы в основном неправильно распределили объект. Фактически, результат следующего обновления состояния:

0: {title: "Do the dishes", done: false, id: 123}
1: {title: "wash car", done: false, id: 423}
2: {title: "Buy pen", done: false, id: 323}
3: {0: "[", 1: "o", 2: "b", 3: "j", 4: "e", 5: "c", 6: "t", 7: " ", 8: "O", 9: "b", 10: "j", 11: "e", 12: "c", 13: "t", 14: "]", done: true}

Это объясняет полученную ошибку unique key.

Одним из возможных решений было бы использование оболочки для обработчика изменений который передает идентификатор элемента и проверенный статус:

function Todo({todo , markComplete}) {
    const handleChange = React.useCallback(e => {
        markComplete(todo.id, e.target.checked);
    }, [todo, markComplete]);

    return (
        <div>
            <input type="checkbox" value={todo.id} onChange={handleChange}></input>
            <li>{todo.title}</li>
            <div className="spacer"></div>
        </div>
    )
}

markComplete:

const markComplete = (id, checked) => {
   setTodos(prevState => prevState.map(it => it.id === id ? ({ ...it, done: checked }) : it));
}

Я бы также рекомендовал использовать useCallback в вышеуказанной функции.

Вот скрипка: https://jsfiddle.net/vdzbj16a/

...