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

Next.js+Redux Toolkit中使用key属性编辑子组件时出现异常

为什么给React列表项添加key属性后会导致编辑时note.title和note.body变为undefined?

我在Notes.tsx文件中使用map函数渲染NoteItem组件,编辑功能基于Redux Toolkit、Redux Thunk及后端API实现。当为NoteItem添加key={item._id}属性后,前端编辑时会出现错误,提示note.body和note.title为undefined;移除key属性后功能正常,但无法部署到Vercel。

在线应用复现步骤:

  • 注册并登录
  • 创建新笔记
  • 尝试编辑该笔记

相关代码

Notes.tsx

{notes
          ? notes.map((item: any) => {
              return <Noteitem key = {item._id} note={item} />;
            })
          : null}

NoteItem.tsx 编辑逻辑

const handleEditConfirm = async (e: any) => {
    e.preventDefault();
    dispatch(editNote(note));
    setShowEditModal(false);
};

/* 部分渲染代码省略*/
<Modal.Footer>
          <Button variant="secondary" onClick={handleEditClose}>
            Close
          </Button>
          <Button
            disabled={note.title.length < 5 || note.body.length < 5}
            variant="primary"
            onClick={handleEditConfirm}
          >
            Save
          </Button>
</Modal.Footer>

notesSlice.tsx 编辑功能

export const editNote = createAsyncThunk("editNote", async (note:any) =>{
    const response = await axios.put("api/notes/editNote",{id:note._id, title:note.title, body:note.body});
    const updatedNote:any = await response.data.note;
    
    return {note: updatedNote};
})

.addCase(editNote.fulfilled, (state, action) =>{
    const id = action.payload.note._id;
    const newNotesData = (state.notesData as any)?.map((note : any) =>
        note._id === id ? action.payload : note
    );

    return {
        ...state,
        notesData : newNotesData
    }
})

后端API(PUT请求)

import { connect } from "@/dbConfig/connect";
import { verifyJwtToken } from "@/lib/jwt";
import Note from "@/models/noteModel";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

connect();
export async function PUT(request: NextRequest) {
  try {
    const { id,title,body } = await request.json();
    const token =  cookies().get("token")!.value;
    if (!token) {
      return NextResponse.json({
        status: 401,
        success: false,
        message: "Invalid auth-token",
      });
    }
    const userData: any = verifyJwtToken(token);

    const note = await Note.findById(id);
    if (!note) {
      return NextResponse.json({
        status: 500,
        success: false,
        message: "Note does not exist",
      });
    }
    
    if (note.user != userData.email) {
      return NextResponse.json({
        status: 500,
        success: false,
        message: "Invalid User",
      });
    }

   const updatedNote = await Note.findByIdAndUpdate(id,{
      title,
      body
    });
    return NextResponse.json({
        note:updatedNote,
        status:200,
        success:true,
        message:"Note successfully updated"
    })

  } catch (error) {
    return NextResponse.json({
      success: false,
      error,
      error_message: "Was not able to delete the note",
    });
  }
}

问题原因及修复方案

核心问题1:Redux状态更新时的结构错误

在editNote.fulfilled的处理逻辑中,你用action.payload替换了匹配到的笔记项,但action.payload的结构是{note: updatedNote},而原数组中的每一项是扁平的note对象。添加key后,React会根据key重新匹配组件并更新props,此时传入NoteItem的note变成了嵌套结构{note: {...}},自然找不到title和body属性,导致报错。

修复:
修改Redux slice中的fulfilled处理,用action.payload.note替换原数组项:

.addCase(editNote.fulfilled, (state, action) =>{
    const id = action.payload.note._id;
    const newNotesData = (state.notesData as any)?.map((note : any) =>
        // 替换为action.payload.note,保持结构一致
        note._id === id ? action.payload.note : note
    );

    return {
        ...state,
        notesData : newNotesData
    }
})

核心问题2:后端返回更新前的文档

findByIdAndUpdate默认返回更新前的文档,这会导致前端拿到旧数据,即使Redux结构正确,也会出现状态不一致的问题。

修复:
添加{new: true}选项,确保返回更新后的文档:

const updatedNote = await Note.findByIdAndUpdate(id,{
  title,
  body
}, {new: true}); // 新增此选项

为什么移除key后功能正常?

移除key时,React会复用组件实例,不会严格根据props更新组件内部状态,相当于“侥幸”保留了正确的note引用,但这违反React列表渲染的最佳实践,且Vercel部署会因为缺少必填的key属性而失败。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:05:22