“TypeError: props.todos.map is not a function” 想不通是什么原因?

时间:2021-01-04 10:02:39

标签: javascript reactjs react-hooks use-state

我正在尝试使用 React.js 创建我的第一个 Todo 列表。我正在尝试从

更改状态

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

5 个答案:

答案 0 :(得分:2)

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

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
    },
];

答案 1 :(得分:1)

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

所以你有两个选择:

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

我希望我的意思很清楚。从手机输入这个并不容易:-)

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

工作应用:Stackblitz

enter image description here

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>
    </>
  );
};

答案 4 :(得分:0)

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。

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

也修改了 handleDelete 函数 :)