【问题标题】:What's render props, how is it different from high order components?什么是渲染道具,它与高阶组件有什么不同?
【发布时间】:2018-07-07 14:15:50
【问题描述】:

似乎render props 到目前为止还没有得到足够的关注,但是,它被著名的 React 库广泛使用,例如 react-router 4react motion 等。而且 React 站点也有一个专门的部分,任何理由这种出现的模式,与众所周知的 HOC(高阶组件)模式相比如何?

【问题讨论】:

    标签: reactjs render


    【解决方案1】:

    为我的研究留下答案,非常欢迎不同的答案和讨论!

    HOC 借鉴了High Order Function 的概念:

    高阶函数(也称为泛函、泛函形式或函子)是至少执行以下操作之一的函数:

    • 将一个或多个函数作为参数(即过程参数),
    • 返回一个函数作为其结果。[有争议 - 讨论]

    HOC

    高阶组件 (HOC) 是 React 中用于重用组件逻辑的高级技术。

    源自Gist

    这个模式是关于STATIC组合的。核心/可重用逻辑封装在 HOC 中,而将活动部分留给组件。

    以 react 路由器中的withRouter 为例:

    withRouter 将在渲染时将更新的匹配、位置和历史道具传递给包装的组件。

    // 这绕过了 shouldComponentUpdate

    withRouter(connect(...)(MyComponent))

    现在你得到一个增强的 MyComponent 回来,它有由路由器 HOC 传递的 { history, match, location, ...connectProps, ...ownProps } 的 props。

    一种常见的方法是

    compose(
      connect( ... ),
      enhance( ... ),
      withRouter( ... ),
      lifecycle( ... ),
      somethingElse( ... )
    )(MyComponent);  
    

    最重要的是,您可以使用compose utility 无限组合这些 HOC,以获得组件的最终增强版本,并且您的组件将从 HOC 返回的新组件中获得有关 redux 存储、反应路由器等的知识.

    这种方法的缺点是:

    1. 组件的行为是在运行时之前定义的,因此失去了 react 渲染生命周期的能力,比如你不能这样做:

      compose(
        this.state.shouldConnect && connect( ... ),
        this.state.shouldEnhance && enhance( ... ),
        this.state.shouldWithRouter && withRouter( ... ),
        ...
      )(MyComponent); 
      

      因为state/props 在您的代码运行之前不可用。

    2. 间接和命名冲突。

    将 HOC 与 ES6 类一起使用会带来许多与 mixin 与 createClass 相同的问题,只是重新安排了一下。

    HOC 引入了很多仪式,因为它们包装组件并创建新组件,而不是混入现有组件中。


    渲染道具

    渲染道具是组件用来知道要渲染什么的函数道具。

    首先被react-motion 采用,早在Dan's Gist 首次提交redux 前几周看到。

    这个模式是关于DYNAMIC组合的。核心/可重用逻辑保留在组件中,而移动部分作为回调属性传递。

    您可以通过渲染道具创建 HOC。

    还是以withRouter为例:

    const withRouter = Component => {
      const C = props => {
        const { wrappedComponentRef, ...remainingProps } = props;
        return (
          <Route
            render={routeComponentProps => (
              <Component
                {...remainingProps}
                {...routeComponentProps}
                ref={wrappedComponentRef}
              />
            )}
          />
        );
      };
      ...
      return hoistStatics(C, Component);
    }; 
    

    反之则不然。

    <Connect render={connectPropsMergedWithState => {
      <Enhance render={enhancePropsMergedWithState => {
        <WithRouter render={routerPropsMergedWithState => {
          <Lifecycle render={lifecyclePropsMergedWithState => {
            <SomethingElse render={somethingElsePropsMergedWithState => {
              ...
            }/>
            ...
          }/>
          ...
        }/>
        ...
      }/>
      ...
    }/>
    

    虽然看起来不太好,但收获不少。

    1. 它具有更好的明确性,因为我们可以看到作为参数传递给渲染道具的确切内容。
    2. 因为 1,它使我们免于潜在的道具碰撞
    3. 它是动态的,我们可以传递任何我们喜欢的东西(包括state/props)来渲染道具。

    众所周知的缺点是performance optimization is tricky,因为要接收的道具被推迟到运行时。过早进行优化可能不是一个好主意,但这可能完全是另一个话题。

    如果你同意 react router 从 3 到 4 的方向移动,render props 可能是你的问题。

    参考资料:

    1. https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce
    2. https://reactrocket.com/post/turn-your-hocs-into-render-prop-components

    【讨论】:

    • 我非常震惊 a) 通常过分热心的模组并没有将问题标记为过于广泛,并且 b) 这篇出色的文章没有任何支持。编辑:啊,我刚刚注意到你写了这个问题并自己回答了;为什么不写一篇博文呢?
    • @ParkerAult 如您所料,如果这个答案能有所帮助,那就太好了,几天前我也有同样的问题,它被标记为太宽泛,因为我最初的尝试是寻找一个答案,我做了一些研究并将其留在这里以获得更好的答案,我仍然认为这是一个合理的问题,有很多来自不同人的好想法:)
    • 我同意这是一个合理的问题,因为它似乎是一种越来越流行的设计模式。我是否正确地说“渲染道具”模式与“作为儿童的功能”相同,因为儿童充当常规道具并且可以传递任何数据(在这种情况下为函数),因此可以调用就像渲染道具一样。
    • @tomhughes 当然,它们完全一样。更重要的是,正如您所说,它们实际上是带有反应糖的非常通用的设计模式。 HOC 只是类似于 *Decorator*/mixin 模式的高阶函数,而渲染道具(子函数)是向组件客户端提供控制反转的回调。
    猜你喜欢
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 2017-12-03
    • 1970-01-01
    • 2023-03-31
    相关资源
    最近更新 更多