React中无法显示Laravel来源动态菜单的问题求助
问题分析
你当前的写法存在核心问题:模块加载时的立即执行异步函数是异步执行的,而createSlice是同步执行的——当createSlice创建切片时,initialState还没被异步请求的结果修改,所以切片的初始状态始终是空数组。而且即使你修改了initialState变量,也不会影响已经创建的Redux切片,因为Redux的状态更新必须通过dispatch action来触发,直接修改变量根本不会同步到Redux store。
解决方案
我们需要用Redux Toolkit的异步Thunk来处理API请求,然后通过reducer更新状态。具体步骤如下:
1. 修改sideMenuSlice.ts,添加更新状态的reducer和异步Thunk
import SidebarDataFetcher from "../Pages/SidebarDataFetcher"; import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import { RootState } from "./store"; import { icons } from "../base-components/Lucide"; export interface Menu { icon: keyof typeof icons; title: string; pathname?: string; subMenu?: Menu[]; ignore?: boolean; } export interface SideMenuState { menu: Array<Menu | "divider">; loading: boolean; // 新增加载状态,方便处理加载中UI error: string | null; // 新增错误状态,处理请求失败情况 } // 初始状态保留空数组,同时添加加载和错误状态 const initialState: SideMenuState = { menu: [], loading: false, error: null, }; // 创建异步Thunk:负责获取侧边栏数据 export const fetchSideMenu = createAsyncThunk( "sideMenu/fetchSideMenu", async () => { const sidebarData = await SidebarDataFetcher(); // 转换数据格式,和你之前的逻辑一致 return Object.entries(sidebarData).flatMap(([key, value]) => Object.entries(value).map(([subKey, subValue]) => ({ icon: "Activity" as keyof typeof icons, title: subKey, subMenu: subValue.map((item) => ({ icon: item.icon as keyof typeof icons, title: item.name, pathname: item.url_slug, })), })) ); } ); export const sideMenuSlice = createSlice({ name: "sideMenu", initialState, reducers: {}, // 处理异步Thunk的三种状态:pending(请求中)、fulfilled(成功)、rejected(失败) extraReducers: (builder) => { builder .addCase(fetchSideMenu.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchSideMenu.fulfilled, (state, action) => { state.loading = false; // 将获取到的数据更新到状态中 state.menu = action.payload; }) .addCase(fetchSideMenu.rejected, (state, action) => { state.loading = false; state.error = action.error.message || "加载侧边栏数据失败"; }); }, }); export const selectSideMenu = (state: RootState) => state.sideMenu.menu; export const selectSideMenuLoading = (state: RootState) => state.sideMenu.loading; export const selectSideMenuError = (state: RootState) => state.sideMenu.error; export default sideMenuSlice.reducer;
2. 在组件中触发异步请求并使用状态
比如在你的侧边栏组件或者App根组件中:
import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { fetchSideMenu, selectSideMenu, selectSideMenuLoading, selectSideMenuError } from "./store/sideMenuSlice"; function Sidebar() { const dispatch = useDispatch(); const menu = useSelector(selectSideMenu); const loading = useSelector(selectSideMenuLoading); const error = useSelector(selectSideMenuError); // 组件挂载时触发数据请求 useEffect(() => { dispatch(fetchSideMenu()); }, [dispatch]); if (loading) { return <div>加载中...</div>; } if (error) { return <div>加载失败:{error}</div>; } // 渲染你的菜单 return ( <div className="sidebar"> {menu.map((item, index) => { if (item === "divider") { return <div key={index} className="divider"></div>; } // 根据Menu类型渲染菜单项和子菜单 return ( <div key={index} className="menu-item"> <span>{item.icon}</span> <span>{item.title}</span> {item.subMenu && ( <div className="sub-menu"> {item.subMenu.map((subItem, subIndex) => ( <a key={subIndex} href={subItem.pathname}> {subItem.icon} {subItem.title} </a> ))} </div> )} </div> ); })} </div> ); } export default Sidebar;
关键说明
- 永远不要试图在模块加载阶段通过异步操作修改Redux的初始状态,Redux的状态更新必须通过dispatch action完成。
createAsyncThunk是Redux Toolkit提供的处理异步逻辑的工具,它会自动生成三个action(pending/fulfilled/rejected),我们可以在extraReducers中监听这些action来更新状态。- 添加
loading和error状态可以让你更好地处理加载中和错误场景,提升用户体验。
内容的提问来源于stack exchange,提问作者Known Dev
相关产品推荐
相关产品推荐

