摘要:精读源码一共行,我们分析一下其精妙的方式。更多讨论讨论地址是精读新用法如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读帮你筛选靠谱的内容。
1 引言
很高兴这一期的话题是由 epitath 的作者 grsabreu 提供的。
前端发展了 20 多年,随着发展中国家越来越多的互联网从业者涌入,现在前端知识玲琅满足,概念、库也越来越多。虽然内容越来越多,但作为个体的你的时间并没有增多,如何持续学习新知识,学什么将会是个大问题。
前端精读通过吸引优质的用户,提供最前沿的话题或者设计理念,虽然每周一篇文章不足以概括这一周的所有焦点,但可以保证你阅读的这十几分钟没有在浪费时间,每一篇精读都是经过精心筛选的,我们既讨论大家关注的焦点,也能找到仓库角落被遗忘的珍珠。
2 概述在介绍 Epitath 之前,先介绍一下 renderProps。
renderProps 是 jsx 的一种实践方式,renderProps 组件并不渲染 dom,但提供了持久化数据与回调函数帮助减少对当前组件 state 的依赖。
RenderProps 的概念react-powerplug 就是一个 renderProps 工具库,我们看看可以做些什么:
{({ on, toggle }) => }
Toggle 就是一个 renderProps 组件,它可以帮助控制受控组件。比如仅仅利用 Toggle,我们可以大大简化 Modal 组件的使用方式:
class App extends React.Component { state = { visible: false }; showModal = () => { this.setState({ visible: true }); }; handleOk = e => { this.setState({ visible: false }); }; handleCancel = e => { this.setState({ visible: false }); }; render() { return (); } } ReactDOM.render(Some contents...
Some contents...
Some contents...
, mountNode);
这是 Modal 标准代码,我们可以使用 Toggle 简化为:
class App extends React.Component { render() { return ({({ on, toggle }) => ( ); } } ReactDOM.render()} Some contents...
Some contents...
Some contents...
, mountNode);
省掉了 state、一堆回调函数,而且代码更简洁,更语义化。
renderProps 内部管理的状态不方便从外部获取,因此只适合保存业务无关的数据,比如 Modal 显隐。RenderProps 嵌套问题的解法
renderProps 虽然好用,但当我们想组合使用时,可能会遇到层层嵌套的问题:
{counter => { {toggle => { ; }}; }}
因此 react-powerplugin 提供了 compose 函数,帮助聚合 renderProps 组件:
import { compose } from "react-powerplug" const ToggleCounter = compose(使用 Epitath 解决嵌套问题, ) {(toggle, counter) => ( )}
Epitath 提供了一种新方式解决这个嵌套的问题:
const App = epitath(function*() { const { count } = yieldconst { on } = yield return ( ) })
renderProps 方案与 Epitath 方案,可以类比为 回调 方案与 async/await 方案。Epitath 和 compose 都解决了 renderProps 可能带来的嵌套问题,而 compose 是通过将多个 renderProps merge 为一个,而 Epitath 的方案更接近 async/await 的思路,利用 generator 实现了伪同步代码。
3 精读Epitath 源码一共 40 行,我们分析一下其精妙的方式。
下面是 Epitath 完整的源码:
import React from "react"; import immutagen from "immutagen"; const compose = ({ next, value }) => next ? React.cloneElement(value, null, values => compose(next(values))) : value; export default Component => { const original = Component.prototype.render; const displayName = `EpitathContainer(${Component.displayName || "anonymous"})`; if (!original) { const generator = immutagen(Component); return Object.assign( function Epitath(props) { return compose(generator(props)); }, { displayName } ); } Component.prototype.render = function render() { // Since we are calling a new function to be called from here instead of // from a component class, we need to ensure that the render method is // invoked against `this`. We only need to do this binding and creation of // this function once, so we cache it by adding it as a property to this // new render method which avoids keeping the generator outside of this // method"s scope. if (!render.generator) { render.generator = immutagen(original.bind(this)); } return compose(render.generator(this.props)); }; return class EpitathContainer extends React.Component { static displayName = displayName; render() { returnimmutagen; } }; };
immutagen 是一个 immutable generator 辅助库,每次调用 .next 都会生成一个新的引用,而不是自己发生 mutable 改变:
import immutagen from "immutagen"; const gen = immutagen(function*() { yield 1; yield 2; return 3; })(); // { value: 1, next: [function] } gen.next(); // { value: 2, next: [function] } gen.next(); // { value: 2, next: [function] } gen.next().next(); // { value: 3, next: undefined }compose
看到 compose 函数就基本明白其实现思路了:
const compose = ({ next, value }) => next ? React.cloneElement(value, null, values => compose(next(values))) : value;
const App = epitath(function*() { const { count } = yield; const { on } = yield ; });
通过 immutagen,依次调用 next,生成新组件,且下一个组件是上一个组件的子组件,因此会产生下面的效果:
yield yield yield// 等价于
到此其源码精髓已经解析完了。
存在的问题crimx 在讨论中提到,Epitath 方案存在的最大问题是,每次 render 都会生成全新的组件,这对内存是一种挑战。
稍微解释一下,无论是通过 原生的 renderProps 还是 compose,同一个组件实例只生成一次,React 内部会持久化这些组件实例。而 immutagen 在运行时每次执行渲染,都会生成不可变数据,也就是全新的引用,这会导致废弃的引用存在大量 GC 压力,同时 React 每次拿到的组件都是全新的,虽然功能相同。
4 总结epitath 巧妙的利用了 immutagen 的不可变 generator 的特性来生成组件,并且在递归 .next 时,将顺序代码解析为嵌套代码,有效解决了 renderProps 嵌套问题。
喜欢 epitath 的同学赶快入手吧!同时我们也看到 generator 手动的步骤控制带来的威力,这是 async/await 完全无法做到的。
是否可以利用 immutagen 解决 React Context 与组件相互嵌套问题呢?还有哪些其他前端功能可以利用 immutagen 简化的呢?欢迎加入讨论。
5 更多讨论讨论地址是:精读《Epitath - renderProps 新用法》 · Issue #106 · dt-fe/weekly
如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/98354.html
摘要:更容易将组件的与状态分离。也就是只提供状态处理方法,不会持久化状态。大体思路是利用共享一份数据,作为的数据源。精读带来的约定函数必须以命名开头,因为这样才方便做检查,防止用判断包裹语句。前端精读帮你筛选靠谱的内容。 1 引言 React Hooks 是 React 16.7.0-alpha 版本推出的新特性,想尝试的同学安装此版本即可。 React Hooks 要解决的问题是状态共享,...
摘要:今天我们就来解读一下的源码。比较有意思,将定时器以方式提供出来,并且提供了方法。实现方式是,在组件内部维护一个定时器,实现了组件更新销毁时的计时器更新销毁操作,可以认为这种定时器的生命周期绑定了组件的生命周期,不用担心销毁和更新的问题。 1. 引言 React PowerPlug 是利用 render props 进行更好状态管理的工具库。 React 项目中,一般一个文件就是一个类,...
摘要:可以看到,这样不仅没有占用组件自己的,也不需要手写回调函数进行处理,这些处理都压缩成了一行。效果通过拿到周期才执行的回调函数。实现等价于的回调仅执行一次时,因此直接把回调函数抛出来即可。 1 引言 上周的 精读《React Hooks》 已经实现了对 React Hooks 的基本认知,也许你也看了 React Hooks 基本实现剖析(就是数组),但理解实现原理就可以用好了吗?学的是...
摘要:实际上是让组件的接收函数,由函数来渲染内容。将通用的逻辑抽象在该组件的内部,然后依据业务逻辑来调用函数内渲染内容的函数,从而达到重用逻辑的目的。当然,上边通过传入了这属于组件的增强功能。还有也提供了一种新模式来解决这个问题。 写业务时,我们经常需要抽象一些使用频率较高的逻辑,但是除了高阶组件可以抽象逻辑,RenderProps也是一种比较好的方法。 RenderProps,顾名思义就是...
阅读 2569·2021-11-24 09:38
阅读 2601·2019-08-30 15:54
阅读 914·2019-08-30 15:52
阅读 1908·2019-08-30 15:44
阅读 2711·2019-08-30 13:48
阅读 767·2019-08-29 16:21
阅读 995·2019-08-29 14:03
阅读 2210·2019-08-28 18:15