React下拉菜单开发问题:数据库嵌套数组映射报错
问题描述
我想实现如下样式的下拉菜单:
- title 1
|-> 带链接的子项列表 - title 2
|-> 带链接的子项列表 - title 3
|-> 带链接的子项列表
现有代码如下:
footerMenu.js
import { useState } from "react"; import FooterItem from "./FooterItem"; const FooterMenu = (props) => { return ( <> <div className="dropdown-menu shadow-lg shadow-slate-300 ml-[40vw] px-12"> <ul className="list-none"> <FooterItem val="About Us" /> <FooterItem val="Careers" /> <FooterItem val="Social Impact" /> <FooterItem val="For Business Partners" /> <FooterItem val="Order and Pickup" /> </ul> </div> </> ); }; export default FooterMenu;
footerItem.js
import { useState } from "react"; import { FooterList } from "./FooterList"; const FooterItem = (props) => { const [open, setOpen] = useState(false); const showDropdown = () => { setOpen(!open); }; return ( <li onClick={showDropdown} className="menu-head p-5"> <h2 className="text-xl font-semibold mb-5">{props.val}</h2> <ul className={`menu-item relative px-1 gap-y-5 ${ open ? "open-list" : "close-list" }`} > <FooterList /> </ul> </li> ); }; export default FooterItem;
footerList.js
import axios from "axios"; export const FooterList = async () => { let response = await axios.get(`http://localhost:5000/footerMenu`); return response.data[0].content.map((val) => { return ( <> <li> <a href={val.link}> {val.subtitle} </a> </li> </> ); });
我的思路是给每个菜单标题在数据库中配置对应子项列表,通过axios拉取数据,但目前出现报错:objects are not valid as a react child(found: Object Promise])。数据库结构为:表中包含title和content字段,content是数组类型,每个元素包含subtitle(子项标题)和link(跳转链接)。同时想了解有没有更简便的下拉菜单实现方式。
解决方案
一、修复Promise报错问题
报错核心原因是FooterList是异步函数,直接作为React组件使用时返回的是Promise对象,而React无法渲染Promise。需要调整数据请求逻辑,改用React的useEffect和useState处理异步数据:
方案1:将请求逻辑整合到FooterItem组件
// 修改后的footerItem.js import { useState, useEffect } from "react"; import axios from "axios"; const FooterItem = (props) => { const [open, setOpen] = useState(false); const [menuItems, setMenuItems] = useState([]); const showDropdown = () => { setOpen(!open); }; useEffect(() => { const fetchMenuItems = async () => { try { // 根据标题筛选对应子项(假设接口支持通过title参数查询) const response = await axios.get(`http://localhost:5000/footerMenu?title=${props.val}`); setMenuItems(response.data[0].content); } catch (err) { console.error("获取菜单数据失败:", err); } }; // 可选优化:仅在下拉展开时请求数据,减少不必要请求 if (open) { fetchMenuItems(); } }, [open, props.val]); return ( <li onClick={showDropdown} className="menu-head p-5"> <h2 className="text-xl font-semibold mb-5">{props.val}</h2> <ul className={`menu-item relative px-1 gap-y-5 ${ open ? "open-list" : "close-list" }`} > {menuItems.map((item, index) => ( <li key={index}> <a href={item.link}>{item.subtitle}</a> </li> ))} </ul> </li> ); }; export default FooterItem;
方案2:保留FooterList组件,改为受控组件
// 修改后的footerList.js export const FooterList = ({ items }) => { return items.map((item, index) => ( <li key={index}> <a href={item.link}>{item.subtitle}</a> </li> )); };
// 对应修改后的footerItem.js import { useState, useEffect } from "react"; import { FooterList } from "./FooterList"; import axios from "axios"; const FooterItem = (props) => { const [open, setOpen] = useState(false); const [menuItems, setMenuItems] = useState([]); const showDropdown = () => { setOpen(!open); }; useEffect(() => { const fetchMenuItems = async () => { const response = await axios.get(`http://localhost:5000/footerMenu?title=${props.val}`); setMenuItems(response.data[0].content); }; if (open) fetchMenuItems(); }, [open, props.val]); return ( <li onClick={showDropdown} className="menu-head p-5"> <h2 className="text-xl font-semibold mb-5">{props.val}</h2> <ul className={`menu-item relative px-1 gap-y-5 ${ open ? "open-list" : "close-list" }`} > <FooterList items={menuItems} /> </ul> </li> ); }; export default FooterItem;
关键注意点:
- React组件不能是异步函数,异步数据请求必须通过
useEffect配合useState处理 - 列表项必须添加
key属性,避免React渲染警告
二、更简便的下拉菜单实现方式
1. CSS原生实现(无需JS控制状态)
适合简单场景,利用CSS:hover伪类实现鼠标悬停展开:
.menu-head { position: relative; } .menu-item { display: none; position: absolute; top: 100%; left: 0; background-color: #fff; padding: 8px 0; } .menu-head:hover .menu-item { display: block; }
这种方式可以直接去掉JS中的open状态管理,大幅简化代码。
2. 使用成熟UI组件库
如果项目允许,直接用现成的UI组件库可以快速实现功能,比如:
- Tailwind UI:适配Tailwind CSS的预制组件,有现成下拉菜单模板
- Ant Design:
Dropdown组件支持多种触发方式(点击/悬停),功能完善 - React Bootstrap:
Dropdown组件开箱即用,风格统一
以Ant Design为例,实现代码非常简洁:
import { Dropdown, Menu } from 'antd'; const FooterMenu = () => { // 可通过接口请求动态生成items数组 const aboutMenuItems = [ { key: '1', label: <a href="/about">公司介绍</a> }, { key: '2', label: <a href="/contact">联系我们</a> }, ]; return ( <Dropdown overlay={<Menu items={aboutMenuItems} />}> <a onClick={(e) => e.preventDefault()}>About Us</a> </Dropdown> ); };
3. 简化自定义逻辑
如果坚持自己实现,可以将菜单数据请求逻辑提升到FooterMenu组件,一次性拉取所有菜单数据后分发给各个FooterItem,避免重复请求接口;或者用React Context统一管理菜单状态,减少组件间的重复代码。
内容的提问来源于stack exchange,提问作者Dreynaldis
相关产品推荐
相关产品推荐

