React列表key警告排查:已设唯一key仍报错(useContext+useReducer)
已在home.js中为列表项设置blog._id作为唯一key,但仍收到以下警告:
react-jsx-dev-runtime.development.js:87 Warning: Each child in a list should have a unique "key" prop.
Check the render method ofHome.
at Blogdetails (http://localhost:3000/static/js/bundle.js:129:5)
项目使用useContext + useReducer管理状态,核心相关代码如下:
home.js
import{useEffect} from 'react' import Blogdetails from '../components/blogdetails' import Form from '../components/form' import { useBlogContext } from '../hooks/useblog' const Home=()=> { const {blogs, dispatch} =useBlogContext() useEffect(()=>{ const fetchBlogs= async()=>{ const blogPost= await fetch('/api/workouts') const res=await blogPost.json() if(blogPost.ok){ dispatch({type:'CREATE_BLOG', payload:res}) } } fetchBlogs() // eslint-disable-next-line },[]) return ( <div className="home"> <div className="worksouts"> {blogs && blogs.map((blog)=>( <Blogdetails key={blog._id} blog={blog}/> ))} </div> <Form/> </div> ) } export default Home
blogcontext.js
import { createContext, useReducer } from 'react'; export const blogContext = createContext(); export const BlogReducer = (state, action) => { switch (action.type) { case 'GET_ALL_BLOGS': return { blogs: action.payload, }; case 'CREATE_BLOG': return { blogs: [action.payload, ...state.blogs], }; default: return state; } }; export const BlogContextprovider = ({ children }) => { const [state, dispatch] = useReducer(BlogReducer, { blogs: [], }); return ( <blogContext.Provider value={{ ...state, dispatch }}> {children} </blogContext.Provider> ); };
其余代码(blogdetails.js、form.js、useblog.js、index.js)见原提问。
核心问题定位
初始加载博客列表时,错误使用了CREATE_BLOG action类型:后端返回的res是所有博客的数组,但CREATE_BLOG的处理逻辑是将payload放到数组开头,导致state中的blogs变成[数组, ...原空数组]的嵌套结构。此时map循环时,第一个元素是整个博客数组而非单个博客对象,blog._id不存在(或为undefined),触发key重复警告。
修复步骤
1. 修正初始加载的action类型
在home.js的useEffect中,将dispatch的action类型改为GET_ALL_BLOGS:
// home.js 中 useEffect 内的修改 if(blogPost.ok){ dispatch({type:'GET_ALL_BLOGS', payload:res}) // 替换 CREATE_BLOG 为 GET_ALL_BLOGS }
GET_ALL_BLOGS会直接将后端返回的博客数组赋值给state.blogs,保证结构为单个博客对象的数组。
2. 验证数组结构
在home.js的return语句前添加日志,确认blogs结构正确:
console.log('当前blogs结构:', blogs)
正常输出应为类似:[{_id: '123', title: '测试标题', des: '测试描述'}, ...]。
3. 可选:增加边界处理
为避免后端返回异常数据(如无_id的项),可在map时过滤无效项:
{blogs && blogs.map((blog)=>{ if(!blog?._id) return null; return <Blogdetails key={blog._id} blog={blog}/> })}
补充说明
添加新博客时使用CREATE_BLOG是正确的,因为此时payload是单个博客对象,[action.payload, ...state.blogs]会将新博客添加到数组开头,保持结构正常。
内容的提问来源于stack exchange,提问作者me boros

