原文链接: https://medium.com/@jrwebdev/...
和之前的文章一样,本文也要求你对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 =( Component: React.ComponentType
) => class MakeCounter extends React.Component< Subtract
& 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 (
); } };
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{ 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) => ({injectedProps => ( );{injectedProps.value})}
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) => ({injectedProps => ( );)}Some other value: {props.value}{injectedProps.value}{props.minCounterValue !== undefined ? (Min value: {props.minCounterValue}) : null} {props.maxCounterValue !== undefined ? (Max value: {props.maxCounterValue}) : null}
有了所有这些好处,特别是更简单的输入,那么为什么不一直使用render props呢?当然可以,这样做不会有任何问题,但要注意render props组件的一些问题。
首先,这里有一个关注点以外的问题;MakeCounter组件现在被放在了Counter组件内而不是包装了它,这使得隔离测试这两个组件更加困难。其次,由于props被注入到组件的渲染函数中,因此不能在生命周期方法中使用它们(前提是计数器被更改为类组件)。
这两个问题都很容易解决,因为您可以使用render props组件简单地生成一个新组件:
interface CounterProps extends InjectedCounterProps { style: React.CSSProperties; } const Counter = (props: CounterProps) => ({props.value}); interface WrappedCounterProps extends CounterProps { minValue?: number; maxValue?: number; } const WrappedCounter = ({ minValue, maxValue, ...props }: WrappedCounterProps) => ({injectedProps => );}
另一个问题是,一般来说,它不太方便,现在使用者需要编写很多样板文件,特别是如果他们只想将组件包装在一个多带带的临时文件中并按原样使用props。这可以通过从render props组件生成HOC来补救:
import { Subtract, Omit } from "utility-types"; import MakeCounter, { MakeCounterProps, InjectedCounterProps } from "./MakeCounter"; type MakeCounterHocProps = Omit; const makeCounter = ( Component: React.ComponentType
): React.SFC
& MakeCounterHocProps> => ({ minValue, maxValue, ...props }: MakeCounterHocProps) => ( {injectedProps => );}
在这里,上一篇文章的技术,以及render props组件的现有类型,被用来生成HOC。这里唯一需要注意的是,我们必须从HOC的props中移除render prop(children),以便在使用时不暴露它:
type MakeCounterHocProps = Omit;
最后,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://www.ucloud.cn/yun/103646.html
摘要:原文链接高阶组件在中是组件复用的一个强大工具。在本文中,高阶组件将会被分为两种基本模式,我们将其命名为和用附加的功能来包裹组件。这里我们使用泛型表示传递到的组件的。在这里,我们定义从返回的组件,并指定该组件将包括传入组件的和的。 原文链接:https://medium.com/@jrwebdev/... 高阶组件(HOCs)在React中是组件复用的一个强大工具。但是,经常有开发者在...
摘要:致力于为应用提供一个类型安全表达力强可组合的状态管理方案。是一组的命名空间。是内置组件的镜像,但允许组件的额外接受类型的数据。这次内容更新,是由组件处理的。这些小的组件不必知道所有的应用状态数据。这是因为大部分对的研究来自于。 Focal Focal 致力于为 React 应用提供一个类型安全、表达力强、可组合的状态管理方案。 用一个不可变的 (immutable) 、响应式的 (o...
摘要:现已存在许多成熟的状态管理解决方案,还有基于的但对于我个人来说,理想的状态管理工具只需同时满足两个特点简单易用,并且适合中大型项目完美地支持要做到这两点其实并不简单。所以我决定自己造一个可能是基于和最好的状态管理工具 现已存在许多成熟的状态管理解决方案:Redux、Mobx、Mobx-state-tree,还有基于 Redux 的 Dva.js、Rematch... 但对于我个人来说,...
摘要:近两年来一直在关注开发,最近也开始全面应用。首先,我们从无状态组件开始。渲染回调模式有一种重用组件逻辑的设计方式是把组件的写成渲染回调函数或者暴露一个函数属性出来。最后,我们将这个回调函数的参数声明为一个独立的类型。 近两年来一直在关注 React 开发,最近也开始全面应用 TypeScript 。国内有很多讲解 React 和 TypeScript 的教程,但如何将 TypeScri...
阅读 980·2021-10-27 14:15
阅读 2735·2021-10-25 09:45
阅读 1893·2021-09-02 09:45
阅读 3332·2019-08-30 15:55
阅读 1753·2019-08-29 16:05
阅读 3167·2019-08-28 18:13
阅读 3092·2019-08-26 13:58
阅读 415·2019-08-26 12:01