使用 TypeScript 开发 React 函数式组件

目录
  • 前言
  • 如何使用 TypeScript 定义函数式组件
    • 1. 使用 React.FC
    • 2. 使用 JSX.Element
    • 3. 直接定义完整类型
    • 4. 使用 React.PropsWithChildren
  • 使用过程需要注意的点
    • 1. 函数式组件返回值不能是布尔值
    • 2. 无法为组件使用 Array.fill() 填充
    • 3. 支持使用泛型来创建组件

前言

在我们使用 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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React+TypeScript+webpack4多入口配置详解

    资源 React-16.8.* react-router-dom-4.3.* TypeScript-3.5.* webpack-4.* eslint-5.16.* 项目目录 ├── dist # 打包结果目录 │ ├── demo1 //类别demo1的打包结果 │ │ ├── demo1.himl │ │ ├── demo1.js │ │ └── demo1.css │ └── demo2 ... //类别demo2的打包结果 ├── src # 业务资源文件目录 │ ├── category

  • Vite+React+TypeScript手撸TodoList的项目实践

    目录 布局与样式 创建工程 定义全局数据类型 实现步骤 源码地址 布局与样式 一个TodoList长什么样子相信无需多言: 上样式: src/TodoList.css .td-wrapper { width: 700px; margin: 0 auto; } .dp-wrapper { width: 100%; height: 40px; display: flex; margin-top: 10px; } .dp-wrapper input { flex: 4; height: 36px; l

  • React + Typescript领域初学者的常见问题和技巧(最新)

    React + Typescript领域初学者的常见问题和技巧 创建一个联合类型的常量 Key const NAME = { HOGE: "hoge", FUGA: "fuga" } as const; keyof typeof NAME // => "HOGE" | "FUGA" 创建常量值的联合类型 typeof NAME[keyof typeof NAME] // => "hoge" |

  • react+typescript中使用echarts的实现步骤

    安装echarts npm install echarts --save 按需加载Echarts demo echarts.init() API文档 import * as echarts from 'echarts/core' import { BarChart, // 系列类型的定义后缀都为 SeriesOption LineChart, } from 'echarts/charts' import { TitleComponent, // 组件类型的定义后缀都为 ComponentOpti

  • 深入学习TypeScript 、React、 Redux和Ant-Design的最佳实践

    前言 阿特伍德定律,指的是any application that can be written in JavaScript, will eventually be written in JavaScript,意即"任何可以用JavaScript来写的应用,最终都将用JavaScript来写" 在使用新技术的时候,切忌要一步一步的来,如果当你尝试把两门不熟悉的新技术一起结合使用,你很大概率会被按在地上摩擦,会yarn/npm和React脚手架等技术是前提,后面我会继续写PWA深入和No

  • 为react组件库添加typescript类型提示的方法

    以我自己的组件react-better-countdown为例, 首先在package.json里面添加types: types/index.d.ts, , 然后文件目录上添加对应文件夹和文件, 最后是index.d.ts文件的编写,具体看代码: import * as React from 'react'; interface CountdownProps { count?: number; dayText?: string | React.ReactElement; hourText?: s

  • 如何用webpack4.0撸单页/多页脚手架 (jquery, react, vue, typescript)

    1.导语 首先来简单介绍一下webpack:现代 JavaScript 应用程序的 静态模块打包工具 .当 webpack 处理应用程序时,它会在内部构建一个会映射项目所需的每个模块 的依赖图(dependency graph),并生成一个或多个 bundle .webpack4.0出现之后,我们可以不用再引入一个配置文件来打包项目,并且它仍然有着很高的可配置性,可以很好满足我们的需求. 在开始正文之前,首先先来看看我们要实现的成果: 支持ES6+JQuery+Less/Scss的单页/多页脚手

  • React+Typescript实现倒计时Hook的方法

    首先对setInterval做了Hook化封装

  • 使用 TypeScript 开发 React 函数式组件

    目录 前言 如何使用 TypeScript 定义函数式组件 1. 使用 React.FC 2. 使用 JSX.Element 3. 直接定义完整类型 4. 使用 React.PropsWithChildren 使用过程需要注意的点 1. 函数式组件返回值不能是布尔值 2. 无法为组件使用 Array.fill() 填充 3. 支持使用泛型来创建组件 前言 在我们使用 React 开发项目时,使用最多的应该都是组件,组件又分为函数组件和类组件,我们可以这么定义: 定义函数组件: function

  • React函数式组件的性能优化思路详解

    优化思路 主要优化的方向有2个: 减少重新 render 的次数.因为在 React 里最重(花时间最长)的一块就是 reconction(简单的可以理解为 diff),如果不 render,就不会 reconction. 减少计算的量.主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用. 在使用类组件的时候,使用的 React 优化 API 主要是:shouldComponentUpdate和 PureComponent 那么在函数式组件中,我们怎么做性能

  • React 函数式组件和类式组件详情

    目录 前言 1. 函数式组件 2. 类式组件 前言 React 是 组件化的 的 JS 库,组件化 也是 React 的核心思想.使用 React 可以构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI.那么什么是组件呢? 组件是用来实现局部功能效果的代码和资源的集合,包括 html / css / js/ image 等,组件的作用是 简化代码.复用代码.提高运行效率. React 里主要有两种类型的组件: 函数式组件 => 基于函数: 类式组件 => 基于类: 1. 函数式组件 函

  • React函数式组件Hook中的useEffect函数的详细解析

    目录 前言 useEffect的作用 useEffect的使用? 1.class组件 2.函数式组件 总结 前言 React函数式编程没有生命周期,因此需要借助useEffect来实现. useEffect的作用 发ajax请求获取数据 设置订阅/获取定时器 手动更改真实DOM useEffect的使用? 1.class组件 在class组件中可以使用生命周期函数,知道组件触发的过程. 代码如下(示例): import React, { Component } from 'react' expo

  • React函数式组件Hook中的useState函数的详细解析

    目录 前言 一.什么是函数式组件 二.useState 前言 公司项目需要使用react,而函数式组件也是官方比较推荐的!!!所以学习hooks是很重要的. 一.什么是函数式组件 纯函数组件有以下特点: 没有状态 没有生命周期 没有 this 因存在如上特点,使得纯函数组件只能做UI展示的功能, 涉及到状态的管理与切换就不得不用到类组件或者redux. 但因为简单的页面也是用类组件,同时要继承一个React实例,使得代码会显得很重. 以前我们可以使用class来声明一个组件,其实使用functi

  • React函数式组件与类组件的不同你知道吗

    目录 区别 其他区别 总结 区别 区别 函数组件 类组件 生命周期 无 有 this 无 有 state 无 有 改变state React Hooks:useState this.setState() 性能 高(不用实例化) 低(需要实例化) 其他区别 ProfilePageClass.js import React from 'react'; class ProfilePageClass extends React.Component { showMessage = () => { aler

  • Vue/React子组件实例暴露方法(TypeScript)

    目录 Vue2 Vue3 React 最近几个月都在用TS开发各种项目,框架有涉及到Vue3,React18等:记录一下Vue/React组件暴露出变量/函数的方法的写法: Vue2 回顾一下Vue2 组件暴露出去方法,它并没有约束,写在methods里的方法都能被调用,data里的变量也能被接收: 现拉一个vue 2.6.10的模板下来子组件的数据 父组件获取子组件实例,调用子组件方法等: 控制台输出: 这个输出的子组件实例里包含所有的变量和方法: Vue3 组件通过vue3提供的define

  • Vue.js函数式组件的全面了解

    目录 前言 React 函数式组件 Vue(2.x) 中的函数式组件

  • React函数组件和类组件的区别及说明

    目录 React函数组件和类组件区别 函数组件 类组件 区别 React函数式组件和类组件的优缺点 1.类组件的性能消耗比较大 2.函数式组件性能消耗小 React函数组件和类组件区别 定义组件有两个要求: 组件名称必须以大写字母开头 组件的返回值只能有一个根元素 函数组件 function Welcome (props) {   return <h1>Welcome {props.name}</h1> } ReactDOM.render(<Welcome name='rea

  • 详解使用React进行组件库开发

    最近针对日常业务需求使用react封装了一套[组件库], 大概记录下整个开发过程中的心得.由于篇幅原因,在这里只对开发过程中比较纠结的选型和打包等进行讨论,后续再对具体组件的封装进行讨论. 概述 我们都知道,组件化的开发模式对于我们的开发效率有着极大的提升,针对我们日常使用的基本组件进行封装,可以大量的简化我们对于基本UI的关注度,让我们的工作聚焦在业务逻辑上,很好的分离业务与基础UI的代码,使得整个项目更有调理,这也是我们要进行本组件库开发的原因. 然而现有React开源组件有很多,像ant-

随机推荐