求助:将React类组件转换为函数组件失败(基于旧版Material-UI的折叠菜单组件)
修复后的函数组件实现
我帮你排查了这个问题,类组件转函数组件后展开收起失效,大概率是状态更新逻辑或者闭包引用的问题导致的。下面是修复后的完整函数组件代码,以及关键的修复要点说明:
完整转换后的代码
首先是导入语句(仅替换类组件相关依赖为函数组件所需的useState):
import React, { useState } from "react"; import * as core from "@material-ui/core"; import { myData } from "./myData";
然后是函数组件的完整实现:
const MyFunctionComponent = () => { // 初始化状态,和类组件的state结构完全一致 const [settings, setSettings] = useState([ { id: "1", open: false }, { id: "2", open: false } ]); // 处理菜单项点击,保持函数式更新确保拿到最新状态 const handleClick = (id) => { setSettings(prevSettings => prevSettings.map(item => item.id === id ? { ...item, open: !item.open } : item ) ); }; return ( <div style={{ marginRight: "15px" }}> <core.List component="nav"> {myData.map(each => { // 提前获取当前菜单项的设置,避免重复调用find,同时做空值校验 const currentSetting = settings.find(item => item.id === each.id); if (!currentSetting) return null; return ( <React.Fragment key={each.id}> <core.ListItem button onClick={() => handleClick(each.id)}> <core.ListItemText inset primary={each.nameHeader} /> {currentSetting.open ? "expanded" : "collapsed"} </core.ListItem> <core.Divider /> <core.Collapse in={currentSetting.open} timeout="auto" unmountOnExit > <core.List component="div" disablePadding> {each.subMenu.map(subData => ( <core.ListItem key={subData.id} button> <core.ListItemText inset primary={subData.name} /> </core.ListItem> ))} </core.List> </core.Collapse> </React.Fragment> ); })} </core.List> </div> ); }; export default MyFunctionComponent;
关键修复要点
状态更新的函数式写法
函数组件中使用useState更新依赖旧状态的新值时,必须用函数式更新(传入接收前状态的函数),这和类组件里setState(state => ...)的逻辑一致。这样能避免闭包陷阱,确保每次更新都基于最新的状态值,这是你转换后功能失效的核心原因。优化状态查找与空值校验
在渲染循环中提前调用find获取当前菜单项的设置,避免重复执行相同的查找逻辑以提升性能;同时增加空值校验,防止myData中存在settings里没有对应id的项时出现Cannot read property 'open' of undefined的错误。移除
this引用
函数组件中直接使用组件内定义的变量和函数,不需要像类组件那样依赖this,这是函数组件的基本语法规范。
额外提醒
如果后续myData中新增了菜单项,记得同步更新useState的初始settings数组,保证每个菜单项都有对应的open状态项,否则新增的菜单项将无法正常展开收起。
内容的提问来源于stack exchange,提问作者hope
相关产品推荐
相关产品推荐

