You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;

修改关键点

  1. 状态提升:把编辑状态从Todo组件移到TodoList,让TodoForm能共享这个状态,判断当前是添加还是编辑模式
  2. 移除冗余渲染:Todo组件不再生成新的TodoForm,点击编辑时只通知父组件更新状态
  3. 输入框复用:TodoForm根据传入的edit状态自动切换占位符、按钮文字和提交逻辑
  4. 优化输入逻辑:修复了原TodoForm中依赖ref取值的写法,改用标准的事件对象获取输入值
  5. 自动重置状态:编辑完成后自动重置编辑状态,回到添加模式

内容的提问来源于stack exchange,提问作者Luciano000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:49:52