能否获取React组件实例返回值 判断其渲染结果是否为null
问题解答
首先说结论:你没法在不违反React渲染规则的前提下,从传入的JSX元素上直接提取组件的返回值。
为什么直接传JSX时没法提前判断返回值
你写的<MenuItems itemsToDisplay={someString} />本质上不是组件执行后的渲染结果,只是一个普通的React元素描述对象,结构大致如下:
{ $$typeof: Symbol(react.element), type: MenuItems, // 存储组件函数本身 props: { itemsToDisplay: someString }, // 存储传入的props key: null, ref: null }
这个对象里根本没有保存组件渲染后的返回值,只有当它真正进入React渲染流程时,React才会调用组件函数生成最终的渲染内容。
你可能会想直接手动调用menuItems.type(menuItems.props)拿返回值做判断,绝对不要这么写:
- 组件内如果用到了任何hooks(useState、useEffect、useContext等),会直接脱离React的hooks调用链路,抛出规则违反错误,产生完全不可预期的bug
- 组件内的副作用会被重复触发,执行时序完全错乱
- 遇到Suspense、错误边界、服务端渲染等场景时会直接崩溃
原来的getMenuItems是普通工具函数,调用就直接返回结果,所以可以直接判断是否为null;但改写成组件后,它的执行时机、上下文完全受React管控,不能手动提前调用。
推荐实现方案
不需要写什么黑科技的extractReturnValue函数,用常规的React设计模式就能解决问题,也不会出现逻辑重复:
方案1:抽离同源判断逻辑(最推荐)
把"是否存在有效菜单项"的判断逻辑抽成独立的纯函数,组件内部和外层渲染判断都复用这一个函数,从根源上避免逻辑重复:
// 抽离无副作用的纯判断逻辑,可全局复用 const hasValidMenuItems = (itemsToDisplay: string) => { // 和MenuItems内部的空判断逻辑完全一致 return itemsToDisplay !== '' } // MenuItems组件内部复用该判断逻辑 const MenuItems: FC<{ itemsToDisplay: string }> = ({ itemsToDisplay }) => { if (!hasValidMenuItems(itemsToDisplay)) { return null } // 原有菜单项渲染逻辑 if (itemsToDisplay === 'slimeItems') { return ( <> <SlimeItem1 /> <SlimeItem2 /> </> ) } // ...其余分支逻辑 } // 使用时,渲染DropdownMenu前先做判断 {hasValidMenuItems(someString) ? ( <DropdownMenu menuItems={<MenuItems itemsToDisplay={someString} />} /> ) : null}
这个方案完全符合React的设计规范,判断逻辑只维护一份,不会出现两边判断不一致的问题。
方案2:调整DropdownMenu的入参设计
如果想把空状态判断逻辑收拢在DropdownMenu内部,可以修改它的props定义,不要直接接收现成的ReactNode,改为接收渲染函数:
type DropdownMenuProps = { renderMenuItems: () => ReactNode } const DropdownMenu: FC<DropdownMenuProps> = ({ renderMenuItems, children }) => { // 按约定调用渲染函数拿到结果,不存在违反React规则的问题 const menuItems = renderMenuItems() if (menuItems === null) { return null } // 正常渲染下拉菜单的逻辑 } // 使用方式 <DropdownMenu renderMenuItems={() => <MenuItems itemsToDisplay={someString} />} />
这种render-prop模式是React里非常成熟的组件设计模式,既保留了组件化的写法,又能灵活拿到渲染内容做前置判断。
不推荐的做法
不要尝试用任何方式"解析"传入的JSX元素、提前执行组件函数拿返回值,这类黑科技写法在React版本升级、组件新增hooks、用到高级特性的时候大概率出问题,维护成本极高。
内容的提问来源于stack exchange,提问作者Michal Kurz
相关产品推荐
相关产品推荐

