React新手求助:将Todo添加到Todos对象时遇问题,求排查
Hey there! Let's break down what's going wrong in your code and get that todo app working right.
1. You're using an object for todos but trying to use array methods
First off, you initialized todos as an empty object ({}), but map() is a method that only works on arrays. That's why you're seeing an error when trying to render your todos—objects don't have a map function. We need to switch todos to an array instead:
const [todos, setTodos] = useState([]) // Empty array instead of object
2. Your handleClick logic is incorrect
Right now, setTodos({ ...todo, todos }) is creating a new object with the current todo string and the old todos object merged in—this isn't how you add items to a list. We need to update the array by adding the new todo to the end. It's also a good idea to give each todo a unique ID (like a timestamp) so React can track them properly for rendering:
function handleClick(e) { e.preventDefault() // Don't add empty todos! if (!todo.trim()) return // Add new todo object with unique ID and text setTodos(prevTodos => [...prevTodos, { id: Date.now(), text: todo }]) // Clear the input after adding setTodo("") }
Using the functional update form (prevTodos => ...) is safer here because it ensures we're always using the latest version of the todos state.
3. Your input isn't a fully controlled component
You have an onChange handler, but you're not binding the input's value to the todo state. This can lead to unexpected behavior. Add the value prop to fix that:
<input onChange={(e) => setTodo(e.target.value)} type="text" placeholder="Add Todo Here" value={todo} // Bind input value to state />
4. Rendering todos needs a key and proper access
When you map over the todos array, React requires each element to have a unique key prop. Also, if we're storing todo objects (not just strings), we need to render the text property instead of the whole object:
{todos.map((todoItem) => ( <div key={todoItem.id}>{todoItem.text}</div> ))}
Full Fixed Code
Here's the complete corrected code:
import React, { useState } from "react" function Content() { const [todo, setTodo] = useState("") // todo: user input const [todos, setTodos] = useState([]) // todos: array of todo objects function handleClick(e) { e.preventDefault() if (!todo.trim()) return // Skip empty todos setTodos(prevTodos => [...prevTodos, { id: Date.now(), text: todo }]) setTodo("") // Clear input after adding } return ( <> <h1>Todos</h1> <form> <input onChange={(e) => setTodo(e.target.value)} type="text" placeholder="Add Todo Here" value={todo} /> <button onClick={handleClick}>Add</button> </form> {todos.map((todoItem) => ( <div key={todoItem.id}>{todoItem.text}</div> ))} </> ) } export default Content
A quick side note: Putting console.log(todos) directly in your JSX will run every time the component renders, which can get noisy. If you want to log the todos state, use a useEffect hook that runs when todos changes instead.
内容的提问来源于stack exchange,提问作者babarosa83

