如何在React中复用添加待办的输入框编辑任务?
React 待办编辑复用添加输入框问题解决
我是React.js新手,从YouTube获取了一个项目代码,当前问题是编辑任务时会生成新的输入框,我希望复用添加待办事项的同一个输入框来完成编辑操作,目前毫无头绪,希望得到帮助。
原组件代码
主组件App.js
import React from 'react'; import './App.css'; import TodoList from './components/TodoList'; function App() { return ( <div className='todo-app'> <TodoList /> </div> ); } export default App;
Todo.jsx组件
import React, { useState } from "react"; import { RiCloseCircleLine } from "react-icons/ri"; import { TiEdit } from "react-icons/ti"; import TodoForm from "./TodoForm"; const Todo = ({ todos, updateTodo, completeTodo }) => { const [edit, setEdit] = useState({ id: null, value: "", }); function updateSubmit(value) { updateTodo(edit.id, value); setEdit({ id: null, value: "", }); } if (editar.id) { return <TodoForm edit={edit} onSubmit={updateSubmit} />; } return todos.map((todo, index) => ( <div className={todo.isComplete ? "todo-row complete" : "todo-row"} key={index} > <div key={todo.id} onClick={() => completeTodo(todo.id)}> {todo.text} </div> <div className="icons"> <TiEdit onClick={() => setEdit({ id: todo.id, value: todo.text })} className="edit-icon" /> </div> </div> )); }; export default Todo;
TodoList.jsx组件
import React, { useState } from "react"; import TodoForm from "./TodoForm"; import Todo from "./Todo"; function TodoList() { const [todos, setTodos] = useState([]); const addTodo = (todo) => { const newTodos = [todo, ...todos]; setTodos(newTodos); }; const updateTodo = (todoId, newValue) => { if (!newValue.text || /^\s*$/.test(newValue.text)) { return; } setTodos((prev) => prev.map((item) => (item.id === todoId ? newValue : item)) ); }; const completeTodo = (id) => { let updatedTodos = todos.map((todo) => { if (todo.id === id) { todo.isComplete = !todo.isComplete; } return todo; }); setTodos(updatedTodos); }; return ( <React.Fragment> <h1>What's the Plan for Today?</h1> <TodoForm onSubmit={addTodo} /> <Todo todos={todos} completeTodo={completeTodo} updateTodo={updateTodo} /> </React.Fragment> ); } export default TodoList;
TodoForm组件
import React, { useState, useEffect, useRef } from "react"; function TodoForm({ edit, onSubmit }) { const [input, setInput] = useState(edit ? edit.value : ""); const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); }); const handleChange = () => { setInput(inputRef.current.value); }; const handleSubmit = (e) => { e.preventDefault(); if (!inputRef.current.value || /^\s*$/.test(inputRef.current.value)) { return; } onSubmit({ id: Math.floor(Math.random() * 10000), text: inputRef.current.value, }); setInput(""); }; return ( <form onSubmit={handleSubmit} className="todo-form"> <React.Fragment> <input placeholder={edit ? "Update your item" : "Add a Todo"} value={input} onChange={handleChange} name="text" className={edit ? "todo-input edit" : "todo-input"} ref={inputRef} /> <button onClick={handleSubmit} className={edit ? "todo-button edit" : "todo-button"} > Update </button> </React.Fragment> </form> ); } export default TodoForm;
解决方案
核心思路是把编辑状态提升到TodoList组件,让原本的添加输入框(TodoForm)根据状态切换成编辑模式,而不是在Todo组件里生成新的输入框。以下是修改后的代码:
修改后的TodoList.jsx
import React, { useState } from "react"; import TodoForm from "./TodoForm"; import Todo from "./Todo"; function TodoList() { const [todos, setTodos] = useState([]); // 新增编辑状态,存储当前编辑的待办ID和内容 const [editState, setEditState] = useState({ id: null, value: "" }); const addTodo = (todo) => { const newTodos = [todo, ...todos]; setTodos(newTodos); }; const updateTodo = (todoId, newValue) => { if (!newValue.text || /^\s*$/.test(newValue.text)) { return; } // 只更新待办的text,保留其他属性 setTodos((prev) => prev.map((item) => item.id === todoId ? { ...item, text: newValue.text } : item ) ); // 编辑完成后重置状态,回到添加模式 setEditState({ id: null, value: "" }); }; const completeTodo = (id) => { let updatedTodos = todos.map((todo) => { if (todo.id === id) { todo.isComplete = !todo.isComplete; } return todo; }); setTodos(updatedTodos); }; // 处理编辑按钮点击,更新编辑状态 const handleEditClick = (todo) => { setEditState({ id: todo.id, value: todo.text }); }; // 提交编辑的方法,传给TodoForm const submitEdit = (value) => { updateTodo(editState.id, value); }; return ( <React.Fragment> <h1>What's the Plan for Today?</h1> {/* 根据编辑状态切换提交逻辑 */} <TodoForm edit={editState} onSubmit={editState.id ? submitEdit : addTodo} /> <Todo todos={todos} completeTodo={completeTodo} handleEditClick={handleEditClick} // 把编辑点击事件传给Todo组件 /> </React.Fragment> ); } export default TodoList;
修改后的Todo.jsx
import React from "react"; import { RiCloseCircleLine } from "react-icons/ri"; import { TiEdit } from "react-icons/ti"; const Todo = ({ todos, completeTodo, handleEditClick }) => { // 移除自己的edit状态,不再渲染新的TodoForm return todos.map((todo, index) => ( <div className={todo.isComplete ? "todo-row complete" : "todo-row"} key={index} > <div key={todo.id} onClick={() => completeTodo(todo.id)}> {todo.text} </div> <div className="icons"> <TiEdit onClick={() => handleEditClick(todo)} // 触发父组件的编辑状态更新 className="edit-icon" /> </div> </div> )); }; export default Todo;
修改后的TodoForm.jsx
import React, { useState, useEffect, useRef } from "react"; function TodoForm({ edit, onSubmit }) { const [input, setInput] = useState(edit ? edit.value : ""); const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); // 当编辑状态变化时,同步更新输入框内容 setInput(edit.value || ""); }, [edit]); // 改成标准的事件对象取值,避免依赖ref的潜在问题 const handleChange = (e) => { setInput(e.target.value); }; const handleSubmit = (e) => { e.preventDefault(); if (!input || /^\s*$/.test(input)) { return; } onSubmit({ // 编辑时用原ID,新增时生成新ID id: edit.id || Math.floor(Math.random() * 10000), text: input, }); setInput(""); }; return ( <form onSubmit={handleSubmit} className="todo-form"> <input placeholder={edit.id ? "Update your item" : "Add a Todo"} value={input} onChange={handleChange} name="text" className={edit.id ? "todo-input edit" : "todo-input"} ref={inputRef} /> {/* 根据状态切换按钮文字 */} <button type="submit" className={edit.id ? "todo-button edit" : "todo-button"} > {edit.id ? "Update" : "Add Todo"} </button> </form> ); } export default TodoForm;
修改关键点
- 状态提升:把编辑状态从Todo组件移到TodoList,让TodoForm能共享这个状态,判断当前是添加还是编辑模式
- 移除冗余渲染:Todo组件不再生成新的TodoForm,点击编辑时只通知父组件更新状态
- 输入框复用:TodoForm根据传入的
edit状态自动切换占位符、按钮文字和提交逻辑 - 优化输入逻辑:修复了原TodoForm中依赖ref取值的写法,改用标准的事件对象获取输入值
- 自动重置状态:编辑完成后自动重置编辑状态,回到添加模式
内容的提问来源于stack exchange,提问作者Luciano000
相关产品推荐
相关产品推荐

