howenhuo

8 天前

如何掌握高级react设计模式: Render Props【译】

本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载

原文链接:How To Master Advanced React Design Patterns: Render Props

在本系列的第1部分中,我们探讨了如何使用复合组件和静态类属性来构建可读可重用的 Stepper 组件。我们看到这种设计模式有一些局限性,因为它不是很灵活; 组件需要是父组件的直接子组件,否则 props 传递会中断。

点击此处查看第1部分

在第2部分中,我们使用新的 Context API 为第1部分的限制提供了优雅且可读的解决方案。可这种设计模式的问题在于它需要一些初始设置才能工作,并且我们的组件不能放在另一个应用程序中。

点击此处查看第2部分

在本部分中,我们将探讨一种设计模式,该模式可以解决到目前为止我们已经确定的所有问题。 它被称为:render props

这种设计模式起初可能有点令人头疼(还记得我们在第2部分中使用的 context consumer 函数吗?)并且为了真正掌握它是如何工作的,我们需要深入了解顶级 React API 以及我们编写的 JSX 代码如何转换为 javascript。接下来,让我们使用一个非常简单的示例,并逐步了解幕后发生的事情。

JSX

JSX 是由 Facebook 工程师设计的 JavaScript语法扩展。我们使用它与 React 来描述 UI 应该是什么样子(有点像模板语言),同时它具有 JavaScript的全部功能。无论何时使用 JSX 编写任何组件,Babel 都会将其编译为 React.createElement() 调用。

我们来看一个非常简单的例子:

上面的两个例子产生相同的结果,父组件简单地转换为 React.createElement() 调用,类型是我们的 Parent 组件,没有属性,也没有子项。

当我们添加子组件时,请注意它本身如何转换为 React.createElement() 调用,上图这种格式创建了我们的 React 组件树。

这里要理解的关键是 Babel 将 Parent 的所有属性编译为一个 props 的 javascript对象; 因为它是纯粹的 javascript对象,所以我们可以传递任何我们想要的东西,例如函数。

在上面的例子中,我们不传递 'string',而是传递了一个返回 'string' 的函数 。当调用该函数时,我们会得到完全相同的结果。

那么上面的例子到底发生了什么呢? 在最初的例子中,我们只是向下传递 'string',将其放在 'div' 中并进行渲染。 然而,在下一个例子中,我们将它作为函数传递并将其放在 'div' 中,但这次是调用函数来实现完全相同的结果。

Render Props

为什么这很重要? 传统上我们将放在父组件中的子组件通过 props.children 渲染出来。

这里要理解的关键是,我们除了设计组件去渲染一个子项,我们还能通过渲染 props中函数 来实现完全相同的结果:

所以,在这个设计模式中,我们渲染 props中函数 而不是子项。 更进一步的想象,我们还能用函数做些什么? 我们可以在调用它们时传递参数:

我们花点时间来消化刚刚发生的事情。 我们传递了一个像以前一样的函数,但不总是返回 'string',而是返回我们在调用它时传入的参数!

等一下,这不是我们在第1部分遇到的问题吗? 为了解决它,我们必须克隆并遍历每个元素,然后传递所需的 props。

现在使用 Render Props 设计模式,我们可以将 props 传递给子组件。

我们可以根据需要命名 props。 因此,不使用 'example',改用更合适的东西:

如果您已经使用过 react router,这可能看起来非常熟悉。 当您需要将 props 传递给 route 时,您需要使用 render 方法。

这就是 render props。 我们不直接渲染组件,而是调用 render 并传入我们想要的任何参数。

让我们回到 Stepper 组件,看看如何利用这种设计模式(我已经删除了所有 context 样板并将 state 添加回 stepper 组件)。

这次不是添加 this.props.children 而是添加 this.props.render(stage,HandleClick)。 我们不再需要向 stepper 组件添加任何子项,我们需要做的就是在 render 中返回相同的标记。

这实现了什么?很棒,现在树中的每个组件都可以访问所有 props。 它本质上给了我们与 context API 相同的 props 曝露,我们不必手动将 props 传递给每个子项。 这种对组件设计的简单调整解决了我们之前提到的所有问题。

然而,使用这种设计模式时要权衡一点,那就是代码的可读性略低于之前。还记得我们在本系列前面看到的奇怪函数吗,那个要在 Context.consumer 组件中添加的函数。

这对我来说很可读; 让我们想想发生了什么。我们只是添加与子项相同效果的函数来代替添加 render 函数。

让我们尝试与之前使用的示例组件对比一下:

左侧,我们像以前一样将函数添加到 render prop。 当 Babel 编译时,该函数被添加到 React.createElement 第二个参数:props。

右侧,我们将函数添加为子项,当编译时被添加到 React.createElement 第三个参数:children。

如何在创建组件时访问该子项函数?

props.children

以类似于调用 render prop 的方式,我们可以调用 props.children (子项是一个函数)并传入我们所需的参数,这不但得到与之前相同的结果,还提高了可读性。

就这样,我们设计出一个高度灵活和极易阅读的组件。用户拥有重新排列子组件的自主权,同时不用担心是否可以访问到它们需要的 props。 最终,它是可重用的,我们可以将它直接放在任何其他应用程序中,无需预先进行任何设置,它都完美地工作。

0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。