什么是渲染道具,它与高阶组件有什么不同?

Posted

技术标签:

【中文标题】什么是渲染道具,它与高阶组件有什么不同?【英文标题】: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(高阶组件)模式相比如何?

【问题讨论】:

【参考方案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 存储、反应路由器等的知识.

这种方法的缺点是:

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

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

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

    间接和命名冲突。

将 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,它使我们免于潜在的道具碰撞。 它是动态的,我们可以传递任何我们喜欢的东西(包括state/props)来渲染道具。

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

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

参考资料:

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

【讨论】:

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

以上是关于什么是渲染道具,它与高阶组件有什么不同?的主要内容,如果未能解决你的问题,请参考以下文章

更新高阶组件中的道具

高阶组件中功能组件的道具

在反应中更改高阶组件中的道具

通过 React 高阶组件传递子道具的正确 TypeScript 类型

[react] 什么渲染劫持?

react基础理论