使用 TypeScript 开发 React 函数式组件

 更新时间:2022年08月29日 08:23:47   作者:pingan8787​​​​​​​  
这篇文章主要介绍了使用 TypeScript开发React函数式组件,文章通过围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

前言

在我们使用 React 开发项目时,使用最多的应该都是组件,组件又分为函数组件类组件,我们可以这么定义:

定义函数组件:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

定义类组件:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

这篇文章我会和大家介绍使用 TypeScript 定义函数式组件的 4 种方法,还有几个使用过程中需要注意的问题。

如何使用 TypeScript 定义函数式组件

函数式组件通常接受一个 props 参数,返回一个 JSX 元素或者 null

当我们需要使用 TypeScript 去定义一个函数式组件时,我们有 4 种方式,4 种方式各有各的优缺点,看具体情况使用。

1. 使用 React.FC

由于 React 不是使用 TypeScript 开发的,使用的是社区开发的 @type/react 包提供的类型,里面有一个通用类型 FC ,允许我们为函数组件添加类型。

type FCProps = { text: string };
// React.FunctionComponent 的简写
const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;

这里的 React.FC 是 React.FunctionComponent 的简写。

当组件包含子元素,TypeScript 会提示警告:

type FCProps = { text: string };
const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;

function App() {
  return (
    <div className="App">
        <FCComponent text="Hello Chris1993.">
            <span>children</span>
        </FCComponent>
    </div>
  );
}

提示警告内容:

Type '{ children: string; text: string; }' is not assignable to type 'IntrinsicAttributes & FCProps'.
  Property 'children' does not exist on type 'IntrinsicAttributes & FCProps'.

现在不推荐使用这个了,具体讨论可以看这两个链接:

2. 使用 JSX.Element

使用 JSX.Element 类型作为函数式组件的返回值类型,当组件的返回值不是 JSX.Element 类型时,TypeScript 就会提示错误。

type FCProps = { text: string };
const ElementComponent = ({ text }: FCProps): JSX.Element => <div>{text}</div>;
function App() {
  return (
    <div className="App">
        <ElementComponent text="Hello Chris1993."></ElementComponent>
    </div>
  );
}

3. 直接定义完整类型

由于 React 组件包含子元素时,会隐式传递一个 children 属性,导致定义的参数类型出错,因此我们可以直接定义一个完整的参数接口,包含了 children 属性的类型:

type FCProps = { text: string; children?: any };
const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;

function App() {
  return (
    <div className="App">
        <FCComponent text="Hello Chris1993.">
            <span>children</span>
        </FCComponent>
    </div>
  );
}

4. 使用 React.PropsWithChildren

第 3 种方法每次都要手动写一个 children 属性类型比较麻烦,这时候我们就可以使用 React.PropsWithChildren 类型,它本身封装了 children 的类型声明:

// react/index.d.ts
type PropsWithChildren<P> = P & { children?: ReactNode };

因此,使用 React.PropsWithChildren 类型定义函数式组件,就不用去处理 children 的类型了:

type IProps = React.PropsWithChildren<{ text: string }>;
const PropsComponent = ({ text }: IProps) => <div>{text}</div>;
function App() {
  return (
    <div className="App">
        <PropsComponent text="Hello Chris1993.">
            <span>children</span>
        </PropsComponent>
    </div>
  );
}

使用过程需要注意的点

1. 函数式组件返回值不能是布尔值

当我们在函数式组件内使用条件语句时,如果返回的是非 JSX 元素或者非 null 的值,React 将会报错:

const ConditionComponent = ({ useRender = false }) =>
  useRender ? <span>Render ConditionComponent</span> : false;// ❌

function App() {
  return (
    <div className="App">
        <ConditionComponent useRender></ConditionComponent>
        {/* 'ConditionComponent' cannot be used as a JSX component.
            Its return type 'false | Element' is not a valid JSX element.
            Type 'boolean' is not assignable to type 'ReactElement<any, any>'.
        */}
    </div>
  );
}

正确的处理方式,应该是让函数式组件返回一个有效的 JSX 元素或者 null:

const ConditionComponent = ({ useRender = false }) =>
  useRender ? <span>Render ConditionComponent</span> : <span>error</span>;// ✅

// or

const ConditionComponent = ({ useRender = false }) =>
  useRender ? <span>Render ConditionComponent</span> : null;// ✅

当然你也不能这样写,当属性 useRender 为 true 时,也会出错:

const ConditionComponent = ({ useRender = false }) =>
  useRender && <span>Render ConditionComponent</span>;// ❌

2. 无法为组件使用 Array.fill() 填充

当我们的组件直接返回 Array.fill() 的结果时,TypeScript 会提示错误。

const ArrayComponent = () => Array(3).fill(<span>Chris1993</span>); // ❌

function App() {
  return (
    <div className="App">
      <ArrayComponent></ArrayComponent>
    </div>
  );
}

提示下面内容:

'ArrayComponent' cannot be used as a JSX component.
  Its return type 'any[]' is not a valid JSX element.
    Type 'any[]' is missing the following properties from type 'ReactElement<any, any>': type, props, key

为了解决这个问题,我们可以定义函数的返回值类型:

const ArrayComponent = () =>
  Array(3).fill(<span>Chris1993</span>) as any as JSX.Element; // ✅

3. 支持使用泛型来创建组件

在使用 TypeScript 开发 React 函数式组件的时候,也可以使用泛型进行约束,声明一个泛型组件(Generic Components),这样可以让我们的组件更加灵活。

可以这样使用:

interface GenericProps<T> {
  content: T;
}
const GenericComponent = <T extends unknown>(props: GenericProps<T>) => {
  const { content } = props;
  const component = <>{content}</>;
  return <div>{component}</div>;
};
function App() {
  return (
    <div className="App">
      { /* Success ✅ */}
      <GenericComponent<number> content={10} />
      { /* Error ❌ Type 'string' is not assignable to type 'number'. */}
      <GenericComponent<number> content={"10"} />
    </div>
  );
}

在 Generic Components 章节中介绍到更高级的使用方式:

interface Props<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

const List = <T extends unknown>(props: Props<T>) => {
  const { items, renderItem } = props;
  const [state, setState] = React.useState<T[]>([]); // You can use type T in List function scope.
  return (
    <div>
      {items.map(renderItem)}
      <button onClick={() => setState(items)}>Clone</button>
      {JSON.stringify(state, null, 2)}
    </div>
  );
};
function App() {
  return (
    <div className="App">
        <List<number>
          items={[1, 2]} // type of 'string' inferred
          renderItem={(item) => (
            <li key={item}>
              {/* Error: Property 'toPrecision' does not exist on type 'string'. */}
              {item.toPrecision(3)}
            </li>
          )}
        />
    </div>
  );
}

到此这篇关于使用 TypeScript 开发 React 函数式组件的文章就介绍到这了,更多相关TypeScript 开发 React内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • javascript匿名函数实例分析

    javascript匿名函数实例分析

    这篇文章主要介绍了javascript匿名函数,以实例形式详细分析了javascript匿名函数的定义、用法及注意事项,具有很好的学习借鉴价值,需要的朋友可以参考下
    2014-11-11
  • 使用base64对图片的二进制进行编码并用ajax进行显示

    使用base64对图片的二进制进行编码并用ajax进行显示

    这篇文章主要介绍了使用base64对图片的二进制进行编码并用ajax进行显示的相关资料,需要的朋友可以参考下
    2017-01-01
  • 详解微信小程序实现仿微信聊天界面(各种细节处理)

    详解微信小程序实现仿微信聊天界面(各种细节处理)

    这篇文章主要介绍了详解微信小程序实现仿微信聊天界面(各种细节处理),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-02-02
  • JavaScript中prompt()函数的用法实战例子

    JavaScript中prompt()函数的用法实战例子

    JavaScript中的prompt是一个函数,用于在浏览器中显示一个对话框,提示用户输入一些信息,这篇文章主要给大家介绍了关于JavaScript中prompt()函数的用法实战,需要的朋友可以参考下
    2023-11-11
  • iframe 异步加载技术及性能分析

    iframe 异步加载技术及性能分析

    我们会经常使用iframes来加载第三方的内容、广告或者插件。使用iframe是因为他可以和主页面并行加载,不会阻塞主页面。
    2011-07-07
  • js中获取时间new Date()的全面介绍

    js中获取时间new Date()的全面介绍

    下面小编就为大家带来一篇js中获取时间new Date()的全面介绍。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • 使用phantomjs进行网页抓取的实现代码

    使用phantomjs进行网页抓取的实现代码

    这篇文章主要介绍了使用phantomjs进行网页抓取的实现代码,需要的朋友可以参考下
    2014-09-09
  • 通过一次报错详细谈谈Point事件

    通过一次报错详细谈谈Point事件

    这篇文章主要给大家介绍了关于如何通过一次报错详细谈谈Point事件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2018-05-05
  • javascript使用prototype完成单继承

    javascript使用prototype完成单继承

    这篇文章主要介绍了javascript使用prototype完成单继承的方法及示例代码,有需要的小伙伴参考下
    2014-12-12
  • bootstrap实现嵌套模态框的实例代码

    bootstrap实现嵌套模态框的实例代码

    这篇文章主要介绍了bootstrap实现嵌套模态框的实例代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01

最新评论