React Todo应用:如何动态修改输入框值更新待办事项名称?
解决React Todo应用中
TypeError: props.todos.map is not a function的问题 嘿,我帮你找到了问题的根源,咱们一步步来解决它!
错误原因
你的报错核心问题出在todoNameChangeHandler方法里:你把原本是数组的todoItem状态直接替换成了字符串!看这段代码:
todoNameChangeHandler = event => { let todoVal = event.target.value; this.state.todoItem.map(todo => { this.setState({ todoItem: todoVal }); // 这里把数组直接改成了字符串! }); };
当用户在修改input里输入内容时,这个方法会把todoItem从数组变成字符串,之后传递给Hello组件的props.todos就不再是数组了,自然没法调用map方法,所以抛出了那个错误。另外这个方法还有个问题:遍历整个todo数组时每次都调用setState,会导致多次不必要的重渲染,也根本没定位到要修改的具体待办项。
解决方案
我们需要调整todo的数据存储结构,让每个待办项成为带唯一标识的对象,这样就能精准修改特定项,同时避免把数组改成字符串。
1. 修改容器组件index.js
- 把todo改成包含
id和text的对象(用时间戳当id可以避免重复) - 重写
todoNameChangeHandler,让它接收待办项的id,只更新对应项的内容 - 优化添加todo的逻辑,避免添加空内容
修改后的代码:
import React, { Component } from "react"; import { render } from "react-dom"; import Hello from "./Hello"; class App extends Component { state = { todoItem: [], todoText: "" }; addTodoHanlder = event => { const todoValue = this.state.todoText.trim(); if (todoValue) { // 过滤空todo this.setState({ todoItem: [...this.state.todoItem, { id: Date.now(), text: todoValue }] }); this.clearTodoText(); } }; clearTodoText() { this.setState({ todoText: "" }); } handleChange = event => { this.setState({ todoText: event.target.value }); }; // 接收todo的id和事件对象,精准更新对应todo的内容 todoNameChangeHandler = (todoId, event) => { const updatedTodos = this.state.todoItem.map(todo => { if (todo.id === todoId) { return { ...todo, text: event.target.value }; } return todo; }); this.setState({ todoItem: updatedTodos }); }; render() { return ( <div> <input type="text" placeholder="Enter Todo" value={this.state.todoText} onChange={this.handleChange} /> <button onClick={this.addTodoHanlder}>AddTodo</button> <Hello todos={this.state.todoItem} changed={this.todoNameChangeHandler} /> </div> ); } } render(<App />, document.getElementById("root"));
2. 修改展示组件Hello.js
- 遍历todo对象数组,给每个input绑定当前todo的text值
- 调用
changed方法时传递当前todo的id,让容器组件知道要修改哪一项 - 用todo的
id作为key(比用index更稳定,符合React最佳实践)
修改后的代码:
import React from "react"; const Hello = props => { return ( <div> <ul> {props.todos.map(todo => { return ( <div key={todo.id}> <p>{todo.text}</p> <input type="text" value={todo.text} onChange={(e) => props.changed(todo.id, e)} /> </div> ); })} </ul> </div> ); }; export default Hello;
额外优化点
- 增加了空todo过滤,避免无效内容
- 用todo的
id作为key,避免列表重排序时的渲染异常 - input绑定当前todo的text值,用户修改时能看到原有内容,体验更流畅
这样修改后,你的todo应用就能正常修改每个待办项的名称,也不会再出现map is not a function的错误啦!
内容的提问来源于stack exchange,提问作者Adarsh
相关产品推荐
相关产品推荐

