使用useContext+useReducer时遇state.blogs不可迭代错误求助
问题描述
原本在home.js中使用useState钩子一切正常,但改用useBlogContext后出现TypeError: state.blogs is not iterable错误,当前使用useContext结合useReducer做状态管理,控制台提示错误出现在<BlogContextprovider>组件中。
控制台错误信息
react-dom.development.js:18687 The above error occurred in the <BlogContextprovider> component: at BlogContextprovider (http://localhost:3000/static/js/bundle.js:458:5) Consider adding an error boundary to your tree to customize error handling behavior.
相关代码文件
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:null }) return( <blogContext.Provider value={{...state, dispatch}}> {children} </blogContext.Provider> ) }
form.js
import { useState } from "react" import { useBlogContext } from '../hooks/useblog' const Form = () => { const{dispatch} = useBlogContext() const [title, setTitle] = useState('') const [des, setDes] = useState('') const [error, setError] = useState(null) const handle= async (e)=>{ e.preventDefault() const blogs= {title,des} const res = await fetch('/api/workouts',{ method:'POST', body:JSON.stringify(blogs), headers:{ 'Content-Type':'application/json' } }) const jon= await res.json() if(!res.ok){ setError(jon.error) } if(res.ok){ setTitle('') setDes('') setError(null) console.log('new blog added',jon) dispatch({type:'CREATE_BLOG', payload:jon}) } } return ( <form onSubmit={handle}> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="title" /> <input type="text" value={des} onChange={(e) => setDes(e.target.value)} placeholder="description" /> <button>add</button> {error && <div className="error">{error}</div>} </form> ) } export default Form
useblog.js
import{blogContext} from "../context/blogcontext" import { useContext } from "react" export const useBlogContext=()=>{ const context = useContext(blogContext) if(!context){ throw Error('exception occured in the context') } return context }
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import { BlogContextprovider } from './context/blogcontext'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BlogContextprovider> <App /> </BlogContextprovider> </React.StrictMode> );
blogdetails.js
const Blogdetails = ({blog}) => { return ( <div className="workout-details"> <h2>{blog.title}</h2> <h5>{blog.des}</h5> </div> ) } export default Blogdetails
问题原因及修复方案
原因
- 初始状态错误:
BlogContextprovider中初始状态blogs设为null,而CREATE_BLOG动作里用...state.blogs展开,null不是可迭代对象,直接展开会触发报错。 - 接口数据处理逻辑错误:
home.js中获取博客列表后,调用的是用于添加单条博客的CREATE_BLOG动作,应该用GET_ALL_BLOGS来覆盖整个博客列表。
修复步骤
1. 修改初始状态(blogcontext.js)
将初始的blogs:null改为空数组,确保初始状态是可迭代对象:
const [state, dispatch]= useReducer(BlogReducer,{ blogs: [] // 替换null为[] })
2. 修正home.js中的dispatch动作
获取博客列表时,发送GET_ALL_BLOGS动作而非CREATE_BLOG:
if(blogPost.ok){ dispatch({type:'GET_ALL_BLOGS', payload:res}) // 替换CREATE_BLOG为GET_ALL_BLOGS }
3. 可选优化:添加边界处理
在BlogReducer的CREATE_BLOG分支中,增加兜底处理,避免state.blogs意外为非数组的情况:
case 'CREATE_BLOG': return{ blogs:[action.payload, ...(state.blogs || [])] // 兜底处理,确保展开的是数组 }
内容的提问来源于stack exchange,提问作者me boros
相关产品推荐
相关产品推荐

