Я работаю над небольшим приложением todo в качестве упражнения с использованием React. У меня есть макет службы вроде этого:
export default class TodoService {
constructor(todos) {
this.todos = new Map();
todos.forEach(todo => {
this.todos.set(todo.id, todo);
});
}
findAll() {
return Array.from(this.todos.values());
}
saveTodo(todo) {
this.todos[todo.id] = todo
}
completeTodo(todo) {
this.todos.delete(todo.id)
}
}
, и в моем приложении React у меня есть состояние, которое содержит задачи:
const [todos, setTodos] = useState([]);
const [flip, flipper] = useState(true);
const completeTodo = (todo) => {
todoService.completeTodo(todo);
flipper(!flip);
}
useEffect(() => {
setTodos(todoService.findAll());
}, [flip])
completeTodo
- это функция, в которую я передаю my Todo
компонент, который будет использоваться, когда я хочу выполнить задачу следующим образом:
import React from "react";
const Todo = ({ todo, completeFn }) => {
return (
<form className="todo">
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
value=""
name={todo.id}
id={todo.id}
onClick={() => {
console.log(`completing todo...`)
completeFn(todo)
}} />
<label className="form-check-label" htmlFor={todo.id}>
{todo.description}
</label>
</div>
</form>
)
}
export default Todo
Итак, что происходит, когда пользователь нажимает флажок, completeFn
вызывается с todo
, он удаляется из служебного объекта, и состояние должно обновляться, но происходит самое странное.
Когда вызывается TodoService.completeTodo()
, задача удаляется правильно, но когда вызывается findAll()
, старая задача все еще там! Если я напишу содержимое в консоль, я увижу, что элемент удаляется, а затем каким-то образом телепортируется обратно, когда я звоню findAll
. Почему это происходит? Я это из-за некоторого React magi c Я не понимаю?
Edit: Что еще более безумно, так это то, что если я изменю это, чтобы использовать эффекты только для начальной загрузки, как это :
const [todos, setTodos] = useState([]);
const completeTodo = (todo) => {
todoService.completeTodo(todo);
setTodos(todoService.findAll());
}
useEffect(() => {
setTodos(todoService.findAll());
}, [])
Я получаю супер странный результат:
Кто-нибудь может мне это объяснить ?
Edit2: Это полностью воспроизводимый пример (без index.html
с <div id="root"></div>
в нем).
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Todo = ({ todo, completeFn }) => {
return (
<div>
<input
type="checkbox"
name={todo.id}
id={todo.id}
onClick={() => {
console.log(`completing todo...`)
completeFn(todo)
}} />
<label className="form-check-label" htmlFor={todo.id}>
{todo.description}
</label>
</div>
)
}
class TodoService {
constructor(todos) {
this.todos = new Map();
todos.forEach(todo => {
this.todos.set(todo.id, todo);
});
}
findAll() {
return Array.from(this.todos.values());
}
saveTodo(todo) {
this.todos[todo.id] = todo
}
completeTodo(todo) {
this.todos.delete(todo.id)
}
}
const App = () => {
let todoService = new TodoService([{
id: 1,
description: "Let's go home."
}, {
id: 2,
description: "Take down the trash"
}, {
id: 3,
description: "Play games"
}]);
const [todos, setTodos] = useState([]);
const [flip, flipper] = useState(true);
const completeTodo = (todo) => {
todoService.completeTodo(todo);
flipper(!flip);
}
useEffect(() => {
setTodos(todoService.findAll());
}, [flip])
return (
<div>
{todos.map(todo => <Todo key={todo.id} todo={todo} completeFn={completeTodo} />)}
</div>
)
};
ReactDOM.render(<App />, document.getElementById("root"));