TypeScript中如何在函数组件JSX块内使用React HOC
React 函数组件内直接使用高阶组件包裹子元素的实现方案
问题描述
我有一个React高阶组件(HOC),尝试在另一个函数组件内部使用它来包裹子元素,代码如下:
// HOC const withSuperpower = <P extends object>(Component: React.ComponentType<P>) => { // do some stuff... return (hocProps: P) => { return ( <> <div id="superpower" /> <Component {...hocProps} /> </> ); }; }; // functional component const Buddy: React.FC<{children: JSX.Element}> = ({children}) => { // do some stuff... return ( <div id="buddy"> { withSuperpower(<>children</>) } {/* 这段代码无法编译 */} </div> ); };
上述代码无法通过编译,我测试发现仅当使用HOC包裹组件的函数/类引用时才能正常运行,示例如下:
export const superBuddy = withSuperpower(Buddy); // 只有这种写法能正常运行
如果要实现内联包裹子元素的效果,我目前必须额外创建第三个中转组件,这种写法存在不必要的冗余,示例如下:
const HOCUser:React.FC<{children: React.ReactNode}> = ({children}) => { return <>{children}</>; } const Buddy: React.FC<{children: JSX.Element}> = ({children}) => { return ( <div id="buddy"> { withSuperpower(HOCUser) } {/* 这种写法可以正常运行 */} </div> ); }
是否存在更优的实现方式,能够直接在函数组件的JSX标签块内使用HOC?
问题原因
你的写法无法编译核心是两个错误:
- 传参类型不匹配:HOC的入参要求是组件类型(组件函数/类的引用),而你传入的
<>children</>是已经实例化完成的React元素,不是可被HOC接收的组件类型。 - 渲染逻辑错误:
withSuperpower(xxx)的返回值是一个新的组件函数,不能直接放在JSX的{}中渲染——{}仅支持放入可直接渲染的值(React元素、字符串、数字、null等),组件必须通过JSX标签形式(<Component />)实例化后才能渲染。
可行方案
方案1:内联定义匿名组件(零额外冗余)
你不需要把中转组件抽离到顶层定义,直接在组件内创建匿名组件传给HOC,再实例化渲染即可,没有额外冗余代码:
const Buddy: React.FC<{children: JSX.Element}> = ({children}) => { // 内联传入匿名组件,调用HOC生成包装后的组件 const WrappedChild = withSuperpower(() => <>{children}</>); return ( <div id="buddy"> {/* 用JSX标签形式实例化组件后渲染 */} <WrappedChild /> </div> ); };
如果担心组件重复创建带来性能损耗,可以用useMemo缓存生成的包装组件:
const WrappedChild = useMemo(() => withSuperpower(() => <>{children}</>), [children]);
方案2:封装通用包装组件
如果你需要频繁内联使用这个HOC,可以基于HOC封装一个普通的包装组件,直接接收children完成包裹,后续使用和普通组件没有区别:
// 原有HOC保留,新增通用包装组件 const SuperpowerWrapper: React.FC<{children: React.ReactNode}> = ({children}) => { const Wrapped = withSuperpower(({children}: {children: React.ReactNode}) => <>{children}</>); return <Wrapped>{children}</Wrapped>; }; // 使用时直接嵌套即可 const Buddy: React.FC<{children: JSX.Element}> = ({children}) => { return ( <div id="buddy"> <SuperpowerWrapper>{children}</SuperpowerWrapper> </div> ); };
方案3:逻辑复用优先用自定义Hook替代HOC
如果你的HOC核心作用是做逻辑注入而非固定渲染结构,对于函数组件来说,自定义Hook是比HOC更轻量、更适合内联使用的复用方式,完全不存在类型适配问题:
// 把HOC中的逻辑抽成Hook const useSuperpower = () => { // 原HOC中的逻辑处理放在这里 return { // 需要注入的状态、方法等 }; }; // 直接在组件内调用即可 const Buddy: React.FC<{children: JSX.Element}> = ({children}) => { useSuperpower(); return ( <div id="buddy"> <div id="superpower" /> {children} </div> ); };
内容的提问来源于stack exchange,提问作者Mark Vital
相关产品推荐
相关产品推荐

