侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

TypeScript中的React Render Props

前端  /  管理员 发布于 6年前   289

和之前的文章一样,本文也要求你对render props有一些知识背景,如果没有官方文档可能会对你有很大的帮助。本文将会使用函数作为children的render props模式以及结合react的context API来作为例子。如果你想使用类似于render这样子的render props,那也只需要把下面例子的children作为你要渲染的props即可。

为了展示render props,我们将要重写之前文章的makeCounter HOC。这里先展示HOC的版本:

export interface InjectedCounterProps {  value: number;  onIncrement(): void;  onDecrement(): void;}interface MakeCounterProps {  minValue?: number;  maxValue?: number;}interface MakeCounterState {  value: number;}const makeCounter = <P extends InjectedCounterProps>(  Component: react.ComponentType<P>) =>  class MakeCounter extends React.Component<    Subtract<P, InjectedCounterProps> & MakeCounterProps,    MakeCounterState  > {    state: MakeCounterState = {      value: 0,    };    increment = () => {      this.setState(prevState => ({        value:          prevState.value === this.props.maxValue            ? prevState.value            : prevState.value + 1,      }));    };    decrement = () => {      this.setState(prevState => ({        value:          prevState.value === this.props.minValue            ? prevState.value            : prevState.value - 1,      }));    };    render() {      const { minValue, maxValue, ...props } = this.props;      return (        <Component          {...props as P}          value={this.state.value}          onIncrement={this.increment}          onDecrement={this.decrement}        />      );    }  };

HOC向组件注入了value和两个回调函数(onIncrement 和 onDecrement),此外还在HOC内部使用minValue和maxValue两个props而没有传递给组件。我们讨论了如果组件需要知道这些值,如何不传递props可能会出现问题,并且如果使用多个HOC包装组件,注入的props的命名也可能与其他HOC注入的props冲突。

makeCounter HOC将会被像下面这样重写:

interface InjectedCounterProps {  value: number;  onIncrement(): void;  onDecrement(): void;}interface MakeCounterProps {  minValue?: number;  maxValue?: number;  children(props: InjectedCounterProps): jsX.Element;}interface MakeCounterState {  value: number;}class MakeCounter extends React.Component<MakeCounterProps, MakeCounterState> {  state: MakeCounterState = {    value: 0,  };  increment = () => {    this.setState(prevState => ({      value:        prevState.value === this.props.maxValue          ? prevState.value          : prevState.value + 1,    }));  };  decrement = () => {    this.setState(prevState => ({      value:        prevState.value === this.props.minValue          ? prevState.value          : prevState.value - 1,    }));  };  render() {    return this.props.children({      value: this.state.value,      onIncrement: this.increment,      onDecrement: this.decrement,    });  }}

这里有一些需要注意的变化。首先,injectedCounterProps被保留,因为我们需要定义一个props的interface在render props函数调用上而不是传递给组件的props(和HOC一样)。MakeCounter(MakeCounterProps)的props已经改变,加上以下内容:

children(props: InjectedCounterProps): jsX.Element;

这是render prop,然后组件内需要一个函数带上注入的props并返回JSX element。下面是它用来突出显示这一点的示例:

interface CounterProps {  style: React.cssProperties;  minValue?: number;  maxValue?: number;}const Counter = (props: CounterProps) => (  <MakeCounter minValue={props.minValue} maxValue={props.maxValue}>    {injectedProps => (      <div style={props.style}>        <button onClick={injectedProps.onDecrement}> - </button>        {injectedProps.value}        <button onClick={injectedProps.onIncrement}> + </button>      </div>    )}  </MakeCounter>);

MakeCounter自己的组件声明变得简单多了;它不再被包装在函数中,因为它不再是临时的,输入也更加简单,不需要泛型、做差值和类型的交集。它只有简单的MakeCounterProps和MakeCounterState,就像其他任何组成部分一样:

class MakeCounter extends React.Component<  MakeCounterProps,   MakeCounterState>

最后,render()的工作也变少了;它只是一个函数调用并带上注入的props-不需要破坏和对象的props扩展运算符展开了!

return this.props.children({  value: this.state.value,  onIncrement: this.increment,  onDecrement: this.decrement,});

然后,render prop组件允许对props的命名和在使用的灵活性上进行更多的控制,这是和HOC等效的一个问题:

interface CounterProps {  style: React.cssProperties;  value: number;  minCounterValue?: number;  maxCounterValue?: number;}const Counter = (props: CounterProps) => (  <MakeCounter    minValue={props.minCounterValue}    maxValue={props.maxCounterValue}  >    {injectedProps => (      <div>        <div>Some other value: {props.value}</div>        <div style={props.style}>          <button onClick={injectedProps.onDecrement}> - </button>          {injectedProps.value}          <button onClick={injectedProps.onIncrement}> + </button>        </div>        {props.minCounterValue !== undefined ? (          <div>Min value: {props.minCounterValue}</div>        ) : null}        {props.maxCounterValue !== undefined ? (          <div>Max value: {props.maxCounterValue}</div>        ) : null}      </div>    )}  </MakeCounter>);

有了所有这些好处,特别是更简单的输入,那么为什么不一直使用render props呢?当然可以,这样做不会有任何问题,但要注意render props组件的一些问题。

首先,这里有一个关注点以外的问题;MakeCounter组件现在被放在了Counter组件内而不是包装了它,这使得隔离测试这两个组件更加困难。其次,由于props被注入到组件的渲染函数中,因此不能在生命周期方法中使用它们(前提是计数器被更改为类组件)。

这两个问题都很容易解决,因为您可以使用render props组件简单地生成一个新组件:

interface CounterProps extends InjectedCounterProps {  style: React.CSSProperties;}const Counter = (props: CounterProps) => (  <div style={props.style}>    <button onClick={props.onDecrement}> - </button>    {props.value}    <button onClick={props.onIncrement}> + </button>  </div>);interface WrappedCounterProps extends CounterProps {  minValue?: number;  maxValue?: number;}const WrappedCounter = ({  minValue,  maxValue,  ...props}: WrappedCounterProps) => (  <MakeCounter minValue={minValue} maxValue={maxValue}>    {injectedProps => <Counter {...props} {...injectedProps} />}  </MakeCounter>);

另一个问题是,一般来说,它不太方便,现在使用者需要编写很多样板文件,特别是如果他们只想将组件包装在一个单独的临时文件中并按原样使用props。这可以通过从render props组件生成HOC来补救:

import { Subtract, Omit } from 'utility-types';import MakeCounter, { MakeCounterProps, InjectedCounterProps } from './MakeCounter';type MakeCounterHocProps = Omit<MakeCounterProps, 'children'>;const makeCounter = <P extends InjectedCounterProps>(  Component: React.ComponentType<P>): React.SFC<Subtract<P, InjectedCounterProps> & MakeCounterHocProps> => ({  minValue,  maxValue,  ...props}: MakeCounterHocProps) => (  <MakeCounter minValue={minValue} maxValue={maxValue}>    {injectedProps => <Component {...props as P} {...injectedProps} />}  </MakeCounter>);

在这里,上一篇文章的技术,以及render props组件的现有类型,被用来生成HOC。这里唯一需要注意的是,我们必须从HOC的props中移除render prop(children),以便在使用时不暴露它:

type MakeCounterHocProps = Omit<MakeCounterProps, 'children'>;


最后,HOC和render props组件之间的权衡归结为灵活性和便利性。这可以通过首先编写render props组件,然后从中生成HOC来解决,这使使用者能够在两者之间进行选择。这种方法在可重用组件库中越来越常见,例如优秀的render-fns库。

就TypeScript而言,毫无疑问,hocs的类型定义要困难得多;尽管通过这两篇文章中的示例,它表明这种负担是由HOC的提供者而不是使用者承担的。在使用方面,可以认为使用HOC比使用render props组件更容易。

在react v16.8.0之前,我建议使用render props组件以提高键入的灵活性和简单性,如果需要,例如构建可重用的组件库,或者对于简单在项目中使用的render props组件,我将仅从中生成HOC。在react v16.8.0中释放react hook之后,我强烈建议在可能的情况下对两个高阶组件或render props使用它们,因为它们的类型更简单。


原文链接:https://medium.com/@jrwebdev/react-render-props-in-typescript-b561b00bc67c



  • 上一条:
    Vue项目构建持续集成阿里云CDN
    下一条:
    为什么用vue,而不用Jquery了?
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客