是否有用于 React Function 组件的类型,包括返回片段、空值、字符串等?

Posted

技术标签:

【中文标题】是否有用于 React Function 组件的类型,包括返回片段、空值、字符串等?【英文标题】:Is there a typing for React Function components that includes returning fragments, nulls, strings etc? 【发布时间】:2021-09-16 18:19:52 【问题描述】:

我的问题是对这个高度赞成的问题的澄清/更新:

When to use JSX.Element vs ReactNode vs ReactElement?

在我看来,当您想要拥有返回字符串、null 等原语的函数组件时,TypeScript 并不能很好地发挥作用。

例如,假设您有一个需要渲染道具的组件:

import React from "react"; 

type User = 
    id: string; 
    name: string; 
    imgSrc: string; 



type UserProfilePageProps = 
    RenderUserPanel: React.ComponentType<user: User>; 


export const fetchUser = async () : Promise<User> => 
    return 
        id: "123", 
        imgSrc: "placeholdit", 
        name: "Joe Bloggs"
    



const UserProfilePage =  (props: UserProfilePageProps) => 
    const RenderUserPanel = props; 
    const [user, setUser] = React.useState<null |User>(null); 

    React.useEffect(()=> 
        fetchUser().then(user => setUser(user)); 
    , []);

    return <section>
        <h2>User Details</h2>
        user && <RenderUserPanel user = user/>
    </section> 

现在我可以创建应该以四种不同方式实现这个渲染属性的组件:

class ClassBasedUserPanel extends React.Component<user: User> 
    render() 
        return <div> 
                this.props.user.name
        </div>
    


class ClassBasedUserPanelThatReturnsString extends React.Component<user: User> 
    render() 
        return  this.props.user.name; 
    


const FunctionBasedUserPanel = (props: user: User) => 
    return <div> 
      props.user.name
    </div>


const FunctionBasedUserPanelThatReturnsString = (props: user: User) => 
    return  props.user.name;

但是,FunctionBasedUserPnelThatReturnsAString 不适合 React.ComponentType&lt;user: User&gt;;,因为 React.FunctionComponent 必须返回一个 React.ReactElement,而字符串不是。

另一方面,React.ClassComponent 没有这个约束。

const Main = () => 
    return <div> 
        <UserProfilePage RenderUserPanel = ClassBasedUserPanel/>
        <UserProfilePage RenderUserPanel = ClassBasedUserPanelThatReturnsString/>
        <UserProfilePage RenderUserPanel = FunctionBasedUserPanel/>
        
        /* ERROR! */
        <UserProfilePage RenderUserPanel = FunctionBasedUserPanelThatReturnsString/>

    </div> 

基本上,我想要的是一种类型:


type TypeImLookingFor<P = >  = React.ComponentType<P> | (props: P) => ReactNode; 

这存在吗?有什么理由不应该存在吗?

注意:我目前使用的是 React 16。也许这是在 React 17 或 18 中修复的问题。

【问题讨论】:

【参考方案1】:

我现在将为此发布我自己的 React 16 最佳解决方案 - 但我欢迎其他答案,特别是如果 React 17/18 有更新。

按照我的建议扩大类型的问题在于它实际上不起作用。

举个例子:

type TypeImLookingFor<P = >  = React.ComponentType<P> | ((props: P) => React.ReactNode); 

class ComponentA extends React.Component 
    render() 
        return "aaa"; 
    


const ComponentB = () => 
    return "bbb"; 


// They do match the type signature
const A: TypeImLookingFor = ComponentA; 
const B: TypeImLookingFor = ComponentB; 

const Main =  () => 
    return <div> 
        <ComponentA/> 
        
        /* 
        'ComponentB' cannot be used as a JSX component.
        Its return type 'string' is not a valid JSX element.ts(2786) */
        <ComponentB/>
    </div>;


也就是说,如果您想以“JSX-y”方式使用函数组件 - 您不能返回字符串、数字等,即使您可以使用类组件来执行此操作。

我认为最好的解决方案是:

    将函数组件的返回类型明确声明为React.ReactElement 如果他们返回的是字符串或数字等,请将其包装在片段中
const ComponentC = () : React.ReactElement => 
    //Type 'string' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>>'.ts(2322)
    return "aaa"; 


const ComponentD = () : React.ReactElement => 
    return <> aaa </>


const Main2 =  () => 
    return <div> 
        <ComponentA/> 
        <ComponentD/>
    </div>;


【讨论】:

以上是关于是否有用于 React Function 组件的类型,包括返回片段、空值、字符串等?的主要内容,如果未能解决你的问题,请参考以下文章

React - 父类组件功能改变子组件的类

React 组件之 Component PureComponent Function Component

React 组件中是不是有任何必需的 div 包装器

基于父 CSS 状态的条件子动画(带有用于 React 的样式组件)

react入门----组件的基础用法

将 React Hook 和 redux 一起用于 React 功能组件是不是不好?