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

React.js/Next.js报错:TypeError: object null is not iterable问题求助

动态菜单实现中的TypeError问题解决

问题概述

在实现动态菜单逻辑时,使用[...state_name, {}]语法更新状态时触发报错:

TypeError: object null is not iterable (cannot read property Symbol(Symbol.iterator))

通过自定义useFetch钩子的LIST方法能正常从JSON Server获取数据,但循环遍历数据更新menu、subMenu状态时触发上述错误。

错误原因分析

  • 初始值与异步加载问题:useFetch中data的初始值为null,组件首次渲染时直接遍历data会导致报错,且数据加载完成前就执行了遍历逻辑。
  • 状态类型不一致:代码中执行了setSubMenu(null),后续再使用[...subMenu]时,subMenu是null类型,无法进行数组展开操作。
  • 错误的状态更新时机:直接在组件函数体中编写for循环,每次组件渲染都会重复执行循环,且React的状态更新是异步的,循环中依赖当前subMenu、menu状态值更新会导致逻辑混乱,无法得到预期结果。

修复方案

关键改动点

  • 使用useEffect监听data的变化,仅当data存在且加载完成时才处理菜单数据
  • 先将原始数据整理为结构化的菜单对象(包含子菜单),再一次性更新menu状态,避免多次调用setState
  • 保持subMenu的数组类型,处理新的主菜单时重置为空数组,不设置为null

修改后的代码

MenuCreator组件

import { useState, useEffect } from "react";
import useFetch from "../functions/useFetch";

const MenuCreator = () => {
    const [menu, setMenu] = useState([]);
    const { data, loading, error } = useFetch('LIST', '/menuler');

    useEffect(() => {
        // 仅当数据加载完成且存在时处理
        if (!loading && data) {
            const structuredMenu = [];
            // 先筛选所有主菜单
            const mainMenus = data.filter(item => item.main_menu === null);
            
            mainMenus.forEach(mainItem => {
                // 找到当前主菜单对应的所有子菜单
                const subMenus = data.filter(subItem => subItem.main_menu === mainItem.id);
                structuredMenu.push({
                    id: mainItem.id,
                    menu_adi: mainItem.menu_adi,
                    menu_icon: mainItem.menu_icon,
                    menu_link: mainItem.menu_link,
                    subMenus: subMenus.map(sub => ({
                        id: sub.id,
                        menu_adi: sub.menu_adi,
                        main_menu: sub.main_menu,
                        menu_link: sub.menu_link
                    }))
                });
            });

            setMenu(structuredMenu);
        }
    }, [data, loading]);

    console.log(menu);
    return { menu };
}

export default MenuCreator;

useFetch钩子(补充完善)

import { useEffect, useState } from 'react';
import { URL } from '../config/conf';

const useFetch = (type, url = "/") => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                switch(type){
                    case "ADD":
                        const addRes = await fetch(`${URL}${url}`, {
                            method: "POST",
                            headers: {"Content-Type": "application/json"},
                            body: JSON.stringify(data)
                        });
                        if (!addRes.ok) throw new Error("Ekleme İşlemi Başarısız");
                        setData("success");
                        break;
                    case "DELETE":
                        const deleteRes = await fetch(`${URL}${url}`, { method: "DELETE" });
                        if (!deleteRes.ok) throw new Error("Silme İşlemi Başarısız");
                        setData("success");
                        break;
                    case "UPDATE":
                        // 补充UPDATE逻辑
                        break;
                    case "LIST":
                        const listRes = await fetch(`${URL}${url}`);
                        if (!listRes.ok) throw new Error("Veriler Getirilirken Bir Hata Oluştu");
                        const listData = await listRes.json();
                        setData(listData);
                        break;
                }
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        };

        fetchData();
    }, [type, url]);

    return { data, loading, error };
}

export default useFetch;

改动说明

  1. MenuCreator组件:
    • 添加useEffect监听data和loading,确保数据加载完成后再处理菜单结构
    • 使用filter和forEach替代嵌套for循环,逻辑更清晰,避免多次调用setState
    • 直接构建完整的结构化菜单数组,一次性更新menu状态,避免异步状态更新的问题
  2. useFetch钩子:
    • 将所有请求逻辑移入useEffect,避免组件渲染时重复执行请求
    • 使用async/await优化异步代码可读性
    • 统一处理请求错误,确保loading状态在请求结束后更新

内容的提问来源于stack exchange,提问作者Samet ÖZTÜRK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:42:24