React组件间状态传递报错:onSelected不是函数问题排查
问题原因
你触发的错误本质是:实际被点击的FeatureMenu组件,并不是你在SelectionMenu里嵌套的那个实例。
点击CategoryButton时执行了history.push(SomeUrls.FeatureMenu),这会让React Router渲染一个全新的FeatureMenu组件——这个路由加载的组件完全独立于SelectionMenu里的那个,没有接收到onSelected props,所以onSelected是undefined,调用时自然会抛出"not a function"的错误。
解决方案
方案一:用本地状态控制显示(推荐,保持组件关联)
放弃路由跳转,改用本地状态控制FeatureMenu的显示/隐藏,这样始终是同一个嵌套的组件实例,能正常接收onSelected:
修改SelectionMenu.tsx:
export const SelectionMenu: React.FunctionComponent = () => { const history = useHistory(); const [selected, setSelected] = useState<string>(""); // 新增状态控制FeatureMenu显示 const [showFeatureMenu, setShowFeatureMenu] = useState(false); const onSelected = (value: string) => setSelected(value); return ( <SubMenu size="half"> <SubMenuHead> <SubMenuTitle>Selection</SubMenuTitle> <Status>{selected}</Status> </SubMenuHead> <SelectList> <MenuHead className="sub-menu-heading">Select</MenuHead> {/* 把路由跳转改成切换显示状态 */} <CategoryButton onClick={() => setShowFeatureMenu(!showFeatureMenu)}> A Category </CategoryButton> {/* 根据状态显示/隐藏FeatureMenu */} {showFeatureMenu && <FeatureMenu onSelected={onSelected} />} </SelectList> </SubMenu> ); };
这样点击分类按钮时,只是切换子菜单的显示状态,FeatureMenu始终嵌套在SelectionMenu中,能正常拿到onSelected函数,点击选项就能更新顶部的选中状态了。
方案二:用Redux共享状态(适合需要路由跳转的场景)
如果你必须保留路由跳转的逻辑,可以利用你已经在使用的Redux来共享选中状态,代替props传递函数:
- 修改Redux的Selection Slice,新增设置选中值的action:
// 假设你的slice文件路径是src/store/slices/selectionSlice.ts import { createSlice } from '@reduxjs/toolkit'; const selectionSlice = createSlice({ name: 'selection', initialState: { list: [], selectedValue: "" // 新增存储选中值的状态 }, reducers: { // 新增设置选中值的action setSelectedValue: (state, action) => { state.selectedValue = action.payload; } } }); export const { setSelectedValue } = selectionSlice.actions; export default selectionSlice.reducer;
- 更新SelectionMenu,从Redux获取选中值:
export const SelectionMenu: React.FunctionComponent = () => { const history = useHistory(); // 从Redux获取选中值,替代本地state const selected = useAppSelector((state) => state.selection.selectedValue); return ( <SubMenu size="half"> <SubMenuHead> <SubMenuTitle>Selection</SubMenuTitle> <Status>{selected}</Status> </SubMenuHead> <SelectList> <MenuHead className="sub-menu-heading">Select</MenuHead> <CategoryButton onClick={() => history.push(SomeUrls.FeatureMenu)}> A Category </CategoryButton> </SelectList> </SubMenu> ); };
- 修改FeatureMenu,直接调用Redux action更新状态:
import { useDispatch } from 'react-redux'; import { setSelectedValue } from '../store/slices/selectionSlice'; // 替换成你的slice路径 // 不再需要onSelected prop type Props = {}; export const FeatureMenu: React.FunctionComponent<Props> = () => { const history = useHistory(); const dispatch = useDispatch(); const selections = useAppSelector((state) => state.selection.list); const feature = useMemo( () => uniq(selections.map((m) => m.aFeature)), [selections] ); const handleClick = (value: string) => { // 调用Redux action更新选中值 dispatch(setSelectedValue(value)); // 可以跳回SelectionMenu页面 history.goBack(); }; return ( <SelectMenu variants={selectMenu} initial="hidden" animate="visible" exit="hidden"> <SelectList> <MenuHeading className="select-menu-heading">Feature</MenuHeading> {feature.map((f) => ( <SelectListButton key={`${f}`} onClick={() => handleClick(`${f}`)} > {`${f}`} </SelectListButton> )) )} </SelectList> </SelectMenu> ); };
这种方式通过Redux全局共享状态,不管FeatureMenu是路由渲染还是嵌套渲染,都能同步更新选中值,SelectionMenu也能实时获取最新状态。
内容的提问来源于stack exchange,提问作者emH
相关产品推荐
相关产品推荐

