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

使用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

问题原因及修复方案

原因

  1. 初始状态错误:BlogContextprovider中初始状态blogs设为null,而CREATE_BLOG动作里用...state.blogs展开,null不是可迭代对象,直接展开会触发报错。
  2. 接口数据处理逻辑错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 01:02:56