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

PERN栈前端PUT请求Postman正常但调用失效,编辑Todo后值为null

问题根因

你的后端接口本身没有故障,Postman可正常调用已经排除后端问题,故障出在前端state类型不匹配导致传参错误:

  • 初始化description state时,你传入的是整个todo对象,结构为{id: number, todo: string}
  • 但输入框的onChange回调里,你直接执行setDescription(e.target.value),直接把description从对象类型覆盖成了字符串类型,原对象的todo属性直接丢失,此时访问description.todo会得到undefined,序列化后传给后端的字段为空,最终数据库存入null。
  • 你控制台打印的description {id: 56, todo: null}是组件首次渲染时的初始值,输入修改后state已经变成字符串,只是你没有触发更新后的打印而已,并非editText函数未执行。
修复方法

推荐用更简洁的第一种写法,直接让state存待编辑的文本字符串即可:

  1. 把useState初始值改为取todo对象的todo字段:const [description, setDescription] = useState(todo.todo)
  2. 输入框的value属性直接绑定value={description},不需要取.todo属性
  3. 请求体保持const body = { description }即可,此时传给后端的结构和Postman测试的结构完全一致。

修改后的完整组件代码:

export default function EditTodo({ todo }) {
  // 直接初始化state为待编辑的文本内容
  const [description, setDescription] = useState(todo.todo);
  console.log("todos", todo);
  console.log("current edit text:", description);

  const editText = async (id) => {
    try {
      const body = { description };
      await fetch(`http://localhost:5000/todos/${id}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      window.location = "/";
    } catch (error) {
      console.error(error.message);
    }
  };
  return (
    <Fragment>
      <button
        type="button"
        className="btn btn-warning"
        data-toggle="modal"
        data-target="#myModal"
      >
        Edit
      </button>

      <div className="modal" id="myModal">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <h4 className="modal-title">Edit Todo</h4>
              <button
                type="button"
                className="close"
                data-dismiss="modal"
              ></button>
            </div>

            <div className="modal-body">
              <input
                type="text"
                className="form-control"
                value={description}
                onChange={(e) => setDescription(e.target.value)}
              />
            </div>

            <div className="modal-footer">
              <button
                type="button"
                className="btn btn-warning"
                data-dismiss="modal"
                onClick={() => editText(todo.id)}
              >
                Edit
              </button>
              <button
                type="button"
                className="btn btn-danger"
                data-dismiss="modal"
              >
                Close
              </button>
            </div>
          </div>
        </div>
      </div>
    </Fragment>
  );
}

如果你需要保留整个todo对象在state中,只需要修改两处:

  • onChange回调改为更新对象的指定字段:onChange={(e) => setDescription(prev => ({...prev, todo: e.target.value}))}
  • 构造请求体时取对象内的文本值:const body = { description: description.todo }

改完后可以在editText函数内打印body确认结构,只要传参和Postman测试的结构一致,就能正常更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 12:06:26