React JS - 单击后无法维护和呈现状态值

React JS - Can't maintain and render the state values after onclick

我按照教程使用功能组件制作待办事项,当我尝试删除待办事项列表中的项目或将其标记为完成时遇到问题。我尝试在 deleteHandler 和 completeHandler 中都使用 prev 状态值。列表中的值按预期工作,但是当我从列表中删除一项时,整个状态值都被破坏了。我已经记下了我拥有 deleteHandler 和 completeHandler 的组件以及其他两个组件。任何人都请指导我在代码中缺少什么。提前致谢。

我有三个组件(表单、TodoList 和 Todos)。表单组件是那里的父组件,只有我将状态值作为道具传递给 TodoList 和 Todos 组件。

待办组件:

const Todo = ({ text, todo, todos, setTodos }) => {
  const deleteHandler = (prevState) => {
    setTodos(todos.filter((el) => el.id !== todo.id));
    // console.log(todo);
  };

  const completeHandler = () => {
    setTodos(
      todos.map((item) => {
        if (item.id === todo.id) {
          return {
            ...item,
            completed: !item.completed,
          };
        }
        return item;
      })
    );
  };

  return (
    <div className="todo">
      <li className={`todo-item ${todo.completed ? "completed" : ""}`}>
        {text}
      </li>
      <button onClick={completeHandler} className="complete-btn">
        <i className="fas fa-check"></i>
      </button>
      <button onClick={deleteHandler} className="trash-btn">
        <i className="fas fa-trash"></i>
      </button>
    </div>
  );
};

export default Todo;

TodoList 组件:

const TodoList = ({ todos, setTodos, filterTodos }) => {
  // console.log("prp", todos);

  return (
    <div className="todo-container">
      <ul className="todo-list">
        {todos && todos.length
          ? filterTodos.map((todo) => (
              <Todo
                setTodos={setTodos}
                todos={todos}
                todo={todo}
                key={todo.id}
                text={todo.text}
                id={todo.id}
              />
            ))
          : ""}
      </ul>
    </div>
  );
};

export default TodoList;

表单组件:

const Form = () => {
  const [inputText, setInputText] = useState("");
  const [todos, setTodos] = useState([]);
  const [status, setStatus] = useState("all");
  const [filterTodos, setFilterTodos] = useState([]);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const filterHandler = () => {
    switch (status) {
      case "completed":
        setFilterTodos(todos.filter((todo) => todo.completed === true));
        break;
      case "incompleted":
        setFilterTodos(todos.filter((todo) => todo.completed === false));
        break;
      default:
        setFilterTodos(todos);
        break;
    }
  };

  useEffect(() => {
    filterHandler();
  }, [todos, status, filterHandler]);

  const inputHandler = (e) => {
    setInputText(e.target.value);
  };

  const submitTodohandler = (e) => {
    e.preventDefault();
    setTodos([
      ...todos,
      { text: inputText, completed: false, id: Math.random() * 1000 },
    ]);
    setInputText(" ");
  };

  const statusHandler = (e) => {
    console.log(e.target.value);
    setStatus(e.target.value);
  };
  return (
    <form>
      <input
        value={inputText}
        onChange={inputHandler}
        type="text"
        className="todo-iput"
      />
      <button className="todo-button" type="submit" onClick={submitTodohandler}>
        <i className="fas fa-plus-square"></i>
      </button>
      <div className="select">
        <select name="todos" className="filter-todo" onChange={statusHandler}>
          <option value="all">All</option>
          <option value="completed">completed</option>
          <option value="incompleted">Incompleted</option>
        </select>
      </div>
      <TodoList setTodos={setTodos} todos={todos} filterTodos={filterTodos} />
    </form>
  );
};

export default Form;

由于您的 TodoList 在表单标签内,您需要在 deleteHandlercompleteHandler 中添加 preventDefault() 以避免页面刷新。

codesandbox

const deleteHandler = (e) => {
    e.preventDefault();
    setTodos(todos.filter((el) => el.id !== todo.id));
    // console.log(todo);
  };

const completeHandler = (e) => {
    e.preventDefault();
    setTodos(
      todos.map((item) => {
        if (item.id === todo.id) {
          return {
            ...item,
            completed: !item.completed
          };
        }
        return item;
      })
    );
  };