Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

精读《React PowerPlug 源码》

hzy666666 2019-02-25 08:55:00 阅读数:215 评论数:0 点赞数:0 收藏数:0

1. 引言

React PowerPlug 是利用 render props 进行更好状态管理的工具库。

React 项目中,一般一个文件就是一个类,状态最细粒度就是文件的粒度。然而文件粒度并非状态管理最合适的粒度,所以有了 Redux 之类的全局状态库。

同样,文件粒度也并非状态管理的最细粒度,更细的粒度或许更合适,因此有了 React PowerPlug。

比如你会在项目中看到这种眼花缭乱的

state:class App extends React.PureComponent { state = { name = 1 isLoading = false isFetchUser = false data = {} disableInput = false validate = false monacoInputValue = '' value = '' } render () { //// } }

其实真正

App级别的状态并没有那么多,很多 **诸如受控组件

onChange临时保存的无意义 Value 找不到合适的地方存储。**

这时候可以用

Value管理局部状态: {({ value, set, reset }) => ( <> )}

源码

State 只存储一个属性

value,并赋初始值为

initial:export default { state = { value: this.props.initial }; }

方法有

set

reset。

set回调函数触发后调用

setState更新

value。

reset就是调用

set并传入

this.props.initial即可。

2.2. Toggle

Toggle 是最直接利用 Value 即可实现的功能,因此放在 Value 之后说。Toggle 值是 boolean 类型,特别适合配合 Switch 等组件。既然 Toggle 功能弱于 Value,为什么不用 Value 替代 Toggle 呢?这是个好问题,如果你不担心自己代码可读性的话,的确可以永远不用 Toggle。

用法

{({ on, toggle }) => }

源码

核心就是利用 Value 组件,

value重命名为

on,增加了

toggle方法,继承

set

reset方法:export default { toggle: () => set(on => !on); }

理所因当,将 value 值限定在 boolean 范围内。

2.3. Counter

与 Toggle 类似,这也是继承了 Value 就可以实现的功能,计数器。

用法

{({ count, inc, dec }) => ( )}

源码

依然利用 Value 组件,

value重命名为

count,增加了

inc

dec

incBy

decBy方法,继承

set

reset方法。

与 Toggle 类似,Counter 将 value 限定在了数字,那么比如

inc就会这么实现:export default { inc: () => set(value => value + 1); }

这里用到了 Value 组件

set函数的多态用法。一般 set 的参数是一个值,但也可以是一个函数,回调是当前的值,这里返回一个 +1 的新值。

2.4. List

操作数组。

用法

{({ list, pull, push }) => (

{list.map({ tag }) => ( pull(value => value === tag)}> {tag} )}
)}

源码

依然利用 Value 组件,

value重命名为

list,增加了

first

last

push

pull

sort方法,继承

set

reset方法。export default { list: value, first: () => value[0], last: () => value[Math.max(0, value.length - 1)], set: list => set(list), push: (...values) => set(list => [...list, ...values]), pull: predicate => set(list => list.filter(complement(predicate))), sort: compareFn => set(list => [...list].sort(compareFn)), reset };

为了利用 React Immutable 更新的特性,因此将

sort函数由 Mutable 修正为 Immutable,

push

pull同理。

2.5. Set

存储数组对象,可以添加和删除元素。类似 ES6 Set。和 List 相比少了许多功能函数,因此只承担添加、删除元素的简单功能。

用法

需要注意的是,

initial是数组,而不是 Set 对象。 {({ values, remove, add }) => ( {values.map(tag => ( remove(tag)}>{tag} ))} )}

源码

依然利用 Value 组件,

value重命名为

values且初始值为

[],增加了

add

remove

clear

has方法,保留

reset方法。

实现依然很简单,

add

remove

clear都利用 Value 提供的

set进行赋值,只要实现几个操作数组方法即可:const unique = arr => arr.filter((d, i) => arr.indexOf(d) === i); const hasItem = (arr, item) => arr.indexOf(item) !== -1; const removeItem = (arr, item) => hasItem(arr, item) ? arr.filter(d => d !== item) : arr; const addUnique = (arr, item) => (hasItem(arr, item) ? arr : [...arr, item]);

has方法则直接复用

hasItem。核心还是利用 Value 的

set函数一招通吃,将操作目标锁定为数组类型罢了。

2.6. map

Map 的实现与 Set 很像,类似 ES6 的 Map。

用法

与 Set 不同,Map 允许设置 Key 名。需要注意的是,

initial是对象,而不是 Map 对象。 {({ set, get }) => ( set("sounds", c)}> Game Sounds set("music", c)}> Bg Music

You are {focused ? "focusing" : "not focusing"} the input.
)}

源码

依然利用 Value 组件,

value重命名为

focused且初始值为

false,增加了

bind方法。

bind方法与 Active 如出一辙,仅是监听时机变成了

onFocus和

onBlur。

2.10. FocusManager

不知道出于什么考虑,FocusManager 的官方文档是空的,而且 Help wanted。。

正如名字描述的,这是一个 Focus 控制器,你可以直接调用

blur来取消焦点。

用法

笔者给了一个例子,在 5 秒后自动失去焦点: {({ focused, blur, bind }) => (

{ setTimeout(() => { blur(); }, 5000); }} />
You are {focused ? "focusing" : "not focusing"} the input.
)}

源码

依然利用 Value 组件,

value重命名为

focused且初始值为

false,增加了

bind

blur方法。

blur方法直接调用

document.activeElement.blur()来触发其

bind监听的

onBlur达到更新状态的效果。

By the way, 还监听了

onMouseDown与

onMouseUp:export default { bind: { tabIndex: -1, onBlur: () => { if (canBlur) { set(false); } }, onFocus: () => set(true), onMouseDown: () => (canBlur = false), onMouseUp: () => (canBlur = true) } };

可能意图是防止在

mouseDown时触发

blur,因为

focus的时机一般是

mouseDown。

2.11. Hover

与 Focus 类似,只是触发时机为 Hover。

用法

{({ hovered, bind }) => (

You are {hovered ? "hovering" : "not hovering"} this div.
)}

源码

依然利用 Value 组件,

value重命名为

hovered且初始值为

false,增加了

bind方法。

bind方法与 Active、Focus 如出一辙,仅是监听时机变成了

onMouseEnter和

onMouseLeave。

2.12. Touch

与 Hover 类似,只是触发时机为 Hover。

用法

{({ touched, bind }) => (

You are {touched ? "touching" : "not touching"} this div.
)}

源码

依然利用 Value 组件,

value重命名为

touched且初始值为

false,增加了

bind方法。

bind方法与 Active、Focus、Hover 如出一辙,仅是监听时机变成了

onTouchStart和

onTouchEnd。

2.13. Field

与 Value 组件唯一的区别,就是

用法

这个用法和 Value 没区别: {({ value, set }) => ( set(e.target.value)} /> )}

但是用

bind更简单:

{({ bind }) => }

源码

依然利用 Value 组件,

value保留不变,初始值为

'',增加了

bind方法,保留

set

reset方法。

与 Value 的唯一区别是,支持了

bind并封装

onChange监听,与赋值受控属性

value。export default { bind: { value, onChange: event => { if (isObject(event) && isObject(event.target)) { set(event.target.value); } else { set(event); } } } };

2.14. Form

这是一个表单工具,有点类似 Antd 的 Form 组件。

用法

{({ field, values }) => ( { e.preventDefault(); console.log("Form Submission Data:", values); }} >
)}

源码

依然利用 Value 组件,

value重命名为

values且初始值为

{},增加了

setValues

field方法,保留

reset方法。

表单最重要的就是

field函数,为表单的每一个控件做绑定,同时设置一个表单唯一

key:export default { field: id => { const value = values[id]; const setValue = updater => typeof updater === "function" ? set(prev => ({ ...prev, [id]: updater(prev[id]) })) : set({ ...values, [id]: updater }); return { value, set: setValue, bind: { value, onChange: event => { if (isObject(event) && isObject(event.target)) { setValue(event.target.value); } else { setValue(event); } } } }; } };

可以看到,为表单的每一项绑定的内容与 Field 组件一样,只是 Form 组件的行为是批量的。

2.15. Interval

Interval 比较有意思,将定时器以 JSX 方式提供出来,并且提供了

stop

resume方法。

用法

{({ start, stop }) => ( <>

The time is now {new Date().toLocaleTimeString()}
)}

源码

提供了

start

stop

toggle方法。

实现方式是,在组件内部维护一个 Interval 定时器,实现了组件更新、销毁时的计时器更新、销毁操作,可以认为这种定时器的生命周期绑定了 React 组件的生命周期,不用担心销毁和更新的问题。

具体逻辑就不列举了,利用

setInterval

clearInterval函数基本上就可以了。

2.16. Compose

Compose 也是个有趣的组件,可以将上面提到的任意多个组件组合使用。

用法

{(counter, toggle) => ( )}

源码

通过递归渲染出嵌套结构,并将每一层结构输出的值存储到

propsList中,最后一起传递给组件。**这也是为什么每个函数

value一般都要重命名的原因。**

精读《Epitath 源码 - renderProps 新用法》 文章中,笔者就介绍了利用

generator解决高阶组件嵌套的问题。

精读《React Hooks》 文章中,介绍了 React Hooks 已经实现了这个特性。

所以当你了解了这三种 "compose" 方法后,就可以在合适的场景使用合适的 compose 方式简化代码。

3. 总结

看完了源码分析,不知道你是更感兴趣使用这个库呢,还是已经跃跃欲试开始造轮子了呢?不论如何,这个库的思想在日常的业务开发中都应该大量实践。

另外 Hooks 版的 PowerPlug 已经 4 个月没有更新了(非官方):react-powerhooks,也许下一个维护者/贡献者 就是你。讨论地址是:精读《React PowerPlug》 · Issue /#129 · dt-fe/weekly

如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

版权声明
本文为[hzy666666]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/ascoders/p/10429073.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领