"TypeError: props.todos.map is not a function" can't figure out what the cause is?

"TypeError: props.todos.map is not a function" can't figure out what the cause is?

我正在尝试使用 React.js 创建我的第一个待办事项列表。我正在尝试更改状态

const [todos, setTodos] = useState([])

收件人:

const [todos, setTodos] = useState({
    todo: [],
    isCompleted: false,
  })

只是尝试添加 isCompleted 状态。但是,当我更改它时,当 运行 我的应用程序来自以前工作的地图时,我会收到错误消息。错误在标题中。

有人能告诉我哪里出了问题吗?

代码:

TodosApp.js

import React, { useState } from "react"
import Todos from "./Todos"

const TodoApp = () => {
  const [todos, setTodos] = useState({
    todo: [],
    isCompleted: false,
  })
  const [input, setInput] = useState("")

  const handleCurrentInput = (e) => {
    setInput(e.target.value)
  }
  const handleSubmit = (e) => {
    e.preventDefault()
    console.log(input)
    setInput("")
    setTodos({
      ...todos,
      task: input,
      isCompleted: false,
    })
  }

  const handleDelete = ({ index }) => {
    const newTodos = [...todos]
    newTodos.splice(index, 1)
    setTodos(newTodos)
  }

  return (
    <div id="todoForm">
      <div class="container">
        <div class="todo_form">
          <div class="todo_input">
            <form onSubmit={handleSubmit}>
              <input
                type="text"
                id="input_todo"
                onChange={handleCurrentInput}
                value={input}
              />
            </form>
            <Todos todos={todos} handleDelete={handleDelete} />
          </div>
        </div>
      </div>
    </div>
  )
}

export default TodoApp

Todos.js

import React, { useState } from "react"

const Todos = (props) => {
  return (
    <ul>
      {props.todos.map((todo, index) => {
        return (
          <li key={todo}>
            {todo}
            <button onClick={() => props.handleDelete({ index })}>
              Delete
            </button>
          </li>
        )
      })}
    </ul>
  )
}

export default Todos

您的状态是一个包含待办事项数组的对象。这就是您传递给 Todos 组件的内容。

所以你有两个选择:

  1. todos.todos作为道具传递或
  2. (更好的方法)重新考虑你的状态。 isCompleted 似乎它应该是每个待办事项的一部分,因为每个待办事项都应该完成而不是列表本身。如果每个待办事项 isCompleted,则列表已完成 所以你的状态是 const [todos, setTodos] = useState([])

我希望你明白我的意思。从 phone 中输入这个并不容易 :-)

您需要专注于每个 todo 项目,包括 2 个道具 task, isCompleted 而不是 todosisCompleted

const [todos, setTodos] = useState([]);
var newTodo = {
                task: 'React JS',
                isCompleted: false
             };
setTodos([...todos, newTodo]);

那么你的 todos 的结构如下:

[
    {
        task: 'Study React JS',
        isCompleted: false
    },
    {
        task: 'Study React Redux',
        isCompleted: false
    },
];

因为你没有设置正确的方式,todos被错误的值覆盖了。你应该写:

    // handleSubmit
    setTodos(s => {
      ...s,
      task: input,
      isCompleted: false,
    });

    // handleDelete
    const newTodos = [...todos]
    newTodos.splice(index, 1)
    setTodos(s => ({ ...s, todos: newTodos }))

工作应用:Stackblitz

import React, { useState, useEffect } from "react";

const TodoApp = () => {
  /* initialize todos with array 
  instead of an object               */
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState("");

  const handleCurrentInput = e => {
    setInput(e.target.value);
  };
  const handleSubmit = e => {
    e.preventDefault();
    console.log(input);
    /* update the state by appending an object having 
       key todo and isCompleted to copy of our main state, 
       todos.
     */
    setTodos([...todos, { todo: input, isCompleted: false }]);
    setInput("");
  };

  const handleDelete = ({ index }) => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

  useEffect(() => {
    console.log(JSON.stringify(todos));
  }, [todos]);

  return (
    <div id="todoForm">
      <div class="container">
        <div class="todo_form">
          <div class="todo_input">
            <form onSubmit={handleSubmit}>
              <input
                type="text"
                id="input_todo"
                onChange={handleCurrentInput}
                value={input}
              />
            </form>
            <Todos todos={todos} handleDelete={handleDelete} />
          </div>
        </div>
      </div>
    </div>
  );
};

export default TodoApp;

const Todos = props => {
  return (
    <>
      <ul>
        {props.todos.map((todo, index) => {
          return (
            <li key={index}>
              {/**use null propogation to avoid accessing the null todo value which will not exist in first render. */}
              {todo?.todo}
              <button onClick={() => props.handleDelete({ index })}>
                Delete
              </button>
            </li>
          );
        })}
      </ul>
    </>
  );
};

isCompleted 应该与每个待办事项相关联。 因此,您应该使用 todos 作为数组并将对象存储在该数组中。每个对象都有 isCompletedtask 属性 以及唯一的 Id

const [todos, setTodos] = useState([]);

您的提交输入如下所示:

 const handleSubmit = (e) => {
    e.preventDefault();
    const todo = {
      task: input,
      id: new Date().getTime().toString(), 
      isCompleted: false
    };
    const updatedTodos = [...todos, todo];

    setTodos(updatedTodos);

    console.log(updatedTodos);
    setInput("");
  };

注意: 要生成唯一 ID,您可以使用 uuid 库。我在这里使用 id: new Date().getTime().toString().

生成了唯一 ID

完整的工作代码沙盒 LINK: https://codesandbox.io/s/todosissue-2mc26?file=/src/TodoApp.js

也修改了handleDelete函数:)