资讯专栏INFORMATION COLUMN

【译】TypeScript中的React高阶组件

wizChen / 2667人阅读

摘要:原文链接高阶组件在中是组件复用的一个强大工具。在本文中,高阶组件将会被分为两种基本模式,我们将其命名为和用附加的功能来包裹组件。这里我们使用泛型表示传递到的组件的。在这里,我们定义从返回的组件,并指定该组件将包括传入组件的和的。

</>复制代码

  1. 原文链接:https://medium.com/@jrwebdev/...

高阶组件(HOCs)在React中是组件复用的一个强大工具。但是,经常有开发者在结合TypeScript使用中抱怨道很难去为其设置types。

这边文章将会假设你已经具备了HOCs的基本知识,并会根据由浅入深的例子来向你展示如何去为其设置types。在本文中,高阶组件将会被分为两种基本模式,我们将其命名为enhancersinjectors

enhancers:用附加的功能/props来包裹组件。

injectors:向组件注入props。

请注意,本文中的示例并不是最佳实践,本文主要只是展示如何在HOCs中设置types。

Enhancers

我们将从enhancers开始,因为它更容易去设置types。此模式的一个基本示例是一个向组件添加loading props的HOC,并且将其设置为true的时候展示loading图。下面是一个没有types的示例:

</>复制代码

  1. const withLoading = Component =>
  2. class WithLoading extends React.Component {
  3. render() {
  4. const { loading, ...props } = this.props;
  5. return loading ? : ;
  6. }
  7. };

然后是加上types

</>复制代码

  1. interface WithLoadingProps {
  2. loading: boolean;
  3. }
  4. const withLoading =

    (Component: React.ComponentType

    ) =>

  5. class WithLoading extends React.Component

    {

  6. render() {
  7. const { loading, ...props } = this.props;
  8. return loading ? : ;
  9. }
  10. };

这里发生了一些事情,所以我们将把它分解:

</>复制代码

  1. interface WithLoadingProps {
  2. loading: boolean;
  3. }

在这里,声明一个props的interface,将会被添加到被包裹的组件上。

</>复制代码

  1. (Component: React.ComponentType

    )

这里我们使用泛型:P表示传递到HOC的组件的props。React.ComponentType

React.FunctionComponent

| React.ClassComponent

的别名,表示传递到HOC的组件可以是类组件或者是函数组件。

</>复制代码

  1. class WithLoading extends React.Component

在这里,我们定义从HOC返回的组件,并指定该组件将包括传入组件的props(P)和HOC的props(WithLoadingProps)。它们通过 & 组合在一起。

</>复制代码

  1. const { loading, ...props } = this.props;

最后,我们使用loading props有条件地显示加loading图或传递了自己props的组件:

</>复制代码

  1. return loading ? : ;

</>复制代码

  1. 注意:由于typescript中可能存在的bug,因此从typescript v3.2开始,这里需要类型转换(props as p)。

我们的withloading HOC也可以重写以返回函数组件而不是类:

</>复制代码

  1. const withLoading =

    (

  2. Component: React.ComponentType

  3. ): React.FC

    => ({

  4. loading,
  5. ...props
  6. }: WithLoadingProps) =>
  7. loading ? : ;

这里,我们对对象rest/spread也有同样的问题,因此通过设置显式的返回类型React.FC

来解决这个问题,但只能在无状态功能组件中使用WithLoadingProps。

</>复制代码

  1. 注意:React.FCReact.FunctionComponent的缩写。在早期版本的@types/react中,是React.SFCReact.StatelessFunctionalComponent
Injectors

injectors是更常见的HOC形式,但更难为其设置类型。除了向组件中注入props外,在大多数情况下,当包裹好后,它们也会移除注入的props,这样它们就不能再从外部设置了。react redux的connect就是是injector HOC的一个例子,但是在本文中,我们将使用一个更简单的例子,它注入一个计数器值并回调以增加和减少该值:

</>复制代码

  1. import { Subtract } from "utility-types";
  2. export interface InjectedCounterProps {
  3. value: number;
  4. onIncrement(): void;
  5. onDecrement(): void;
  6. }
  7. interface MakeCounterState {
  8. value: number;
  9. }
  10. const makeCounter =

    (

  11. Component: React.ComponentType

  12. ) =>
  13. class MakeCounter extends React.Component<
  14. Subtract,
  15. MakeCounterState
  16. > {
  17. state: MakeCounterState = {
  18. value: 0,
  19. };
  20. increment = () => {
  21. this.setState(prevState => ({
  22. value: prevState.value + 1,
  23. }));
  24. };
  25. decrement = () => {
  26. this.setState(prevState => ({
  27. value: prevState.value - 1,
  28. }));
  29. };
  30. render() {
  31. return (
  32. );
  33. }
  34. };

这里有几个关键区别:

</>复制代码

  1. export interface InjectedCounterProps {
  2. value: number;
  3. onIncrement(): void;
  4. onDecrement(): void;
  5. }

我们给将要注入到组件的props声明一个interface,该接口将被导出,以便这些props可由被HOC包裹的组件使用:

</>复制代码

  1. import makeCounter, { InjectedCounterProps } from "./makeCounter";
  2. interface CounterProps extends InjectedCounterProps {
  3. style?: React.CSSProperties;
  4. }
  5. const Counter = (props: CounterProps) => (
  6. {props.value}
  7. );
  8. export default makeCounter(Counter);

</>复制代码

  1. (Component: React.ComponentType

    )

我们再次使用泛型,但是这次,你要确保传入到HOC的组件包含注入到其中的props,否则,你将收到一个编译错误。

</>复制代码

  1. class MakeCounter extends React.Component<
  2. Subtract,
  3. MakeCounterState
  4. >

HOC返回的组件使用Piotrek Witek’s的utility-types包中的subtract,它将从传入组件的props中减去注入的props,这意味着如果它们设置在生成的包裹组件上,则会收到编译错误:
![TypeScript compilation error when attempting to set value on the wrapped component
](https://cdn-images-1.medium.c...*xTKe3DWJdC7nAVQnM4bvbg.png)

Enhance + Inject

结合这两种模式,我们将在计数器示例的基础上,允许将最小和最大计数器值传递给HOC,而HOC又被它截取并使用,而不将它们传递给组件:

</>复制代码

  1. export interface InjectedCounterProps {
  2. value: number;
  3. onIncrement(): void;
  4. onDecrement(): void;
  5. }
  6. interface MakeCounterProps {
  7. minValue?: number;
  8. maxValue?: number;
  9. }
  10. interface MakeCounterState {
  11. value: number;
  12. }
  13. const makeCounter =

    (

  14. Component: React.ComponentType

  15. ) =>
  16. class MakeCounter extends React.Component<
  17. Subtract & MakeCounterProps,
  18. MakeCounterState
  19. > {
  20. state: MakeCounterState = {
  21. value: 0,
  22. };
  23. increment = () => {
  24. this.setState(prevState => ({
  25. value:
  26. prevState.value === this.props.maxValue
  27. ? prevState.value
  28. : prevState.value + 1,
  29. }));
  30. };
  31. decrement = () => {
  32. this.setState(prevState => ({
  33. value:
  34. prevState.value === this.props.minValue
  35. ? prevState.value
  36. : prevState.value - 1,
  37. }));
  38. };
  39. render() {
  40. const { minValue, maxValue, ...props } = this.props;
  41. return (
  42. );
  43. }
  44. };

这里,Subtract与types交集相结合,将组件自身的props与HOCs自身的props相结合,减去注入组件的props:

</>复制代码

  1. Subtract & MakeCounterProps

除此之外,与其他两种模式相比,没有真正的差异需要强调,但是这个示例确实带来了一些高阶组件的问题。这些并不是真正特定于typescript的,但值得详细说明,以便我们可以讨论如何使用typescript来解决这些问题。

首先,MinValue和MaxValue被HOC拦截,而不是传递给组件。但是,你也许希望它们是这样的,这样你就可以基于这些值禁用递增/递减按钮,或者向用户显示一条消息。如果用HOC,你也可以简单地修改它来注入这些值,但是如果你没有(例如,它来自一个NPM包),这就将会是一个问题。

其次,由HOC注入的prop有一个非常通用的名称;如果要将其用于其他目的,或者如果要从多个HOC注入prop,则此名称可能与其他注入的prop冲突。您可以将名称更改为不太通用的解决方案,但就解决方案而言,这不是一个很好的解决方案!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103632.html

相关文章

  • TypeScript中的React Render Props

    原文链接: https://medium.com/@jrwebdev/... 和之前的文章一样,本文也要求你对render props有一些知识背景,如果没有官方文档可能会对你有很大的帮助。本文将会使用函数作为children的render props模式以及结合React的context API来作为例子。如果你想使用类似于render这样子的render props,那也只需要把下面例子的c...

    GeekGhc 评论0 收藏0
  • 正在暑假中的《课多周刊》(第1期)

    摘要:正在暑假中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。原理微信热更新方案涨知识了,热更新是以后的标配。 正在暑假中的《课多周刊》(第1期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的动力。 远上寒山石径...

    liukai90 评论0 收藏0
  • 正在暑假中的《课多周刊》(第1期)

    摘要:正在暑假中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。原理微信热更新方案涨知识了,热更新是以后的标配。 正在暑假中的《课多周刊》(第1期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的动力。 远上寒山石径...

    yintaolaowanzi 评论0 收藏0
  • 2017-06-28 前端日报

    摘要:前端日报精选我是如何实现的在线升级热更新功能的张鑫旭鑫空间鑫生活翻译表单的运用第期晋升评审的套路异步编程的四种方式黄博客精选组件设计和分解思考掘金中文译使登录页面变得正确掘金前端从强制开启压缩探究插件运行机制掘金个常用的简 2017-06-28 前端日报 精选 我是如何实现electron的在线升级热更新功能的? « 张鑫旭-鑫空间-鑫生活【翻译】React 表单: Refs 的运用【...

    QLQ 评论0 收藏0
  • 】函数式的React

    摘要:高阶组件和高阶函数高阶组件是编程中的常见模式。您可以将上面例子中的高阶函数用函数的方式来重新整理如你所见,这就像是一个高阶函数,这个函数接受一个函数,返回一个新的元素。函数式编程范例旨在避免在应用程序中使用状态。 原文:The functional side of React作者:Andrea Chiarelli译者:博轩 React 是现在最流行的 JavaScript 库之一。使用...

    gnehc 评论0 收藏0

发表评论

0条评论

wizChen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<