摘要:今天我們就來解讀一下的源碼。比較有意思,將定時(shí)器以方式提供出來,并且提供了方法。實(shí)現(xiàn)方式是,在組件內(nèi)部維護(hù)一個(gè)定時(shí)器,實(shí)現(xiàn)了組件更新銷毀時(shí)的計(jì)時(shí)器更新銷毀操作,可以認(rèn)為這種定時(shí)器的生命周期綁定了組件的生命周期,不用擔(dān)心銷毀和更新的問題。
1. 引言
React PowerPlug 是利用 render props 進(jìn)行更好狀態(tài)管理的工具庫。
React 項(xiàng)目中,一般一個(gè)文件就是一個(gè)類,狀態(tài)最細(xì)粒度就是文件的粒度。然而文件粒度并非狀態(tài)管理最合適的粒度,所以有了 Redux 之類的全局狀態(tài)庫。
同樣,文件粒度也并非狀態(tài)管理的最細(xì)粒度,更細(xì)的粒度或許更合適,因此有了 React PowerPlug。
比如你會(huì)在項(xiàng)目中看到這種眼花繚亂的 state:
class App extends React.PureComponent { state = { name = 1 isLoading = false isFetchUser = false data = {} disableInput = false validate = false monacoInputValue = "" value = "" } render () { /**/ } }
其實(shí)真正 App 級(jí)別的狀態(tài)并沒有那么多,很多 諸如受控組件 onChange 臨時(shí)保存的無意義 Value 找不到合適的地方存儲(chǔ)。
這時(shí)候可以用 Value 管理局部狀態(tài):
{({ value, set, reset }) => ( <> > )}
可以看到,這個(gè)問題本質(zhì)上應(yīng)該拆成新的 React 類解決,但這也許會(huì)導(dǎo)致項(xiàng)目結(jié)構(gòu)更混亂,因此 RenderProps 還是必不可少的。
今天我們就來解讀一下 React PowerPlug 的源碼。
2. 精讀 2.1. Value這是一個(gè)值操作的工具,功能與 Hooks 中 useState 類似,不過多了一個(gè) reset 功能(Hooks 其實(shí)也未嘗不能有,但 Hooks 確實(shí)沒有 Reset)。
用法源碼{({ value, set, reset }) => ( <> > )}
源碼地址
原料:無
State 只存儲(chǔ)一個(gè)屬性 value,并賦初始值為 initial:
export default { state = { value: this.props.initial }; }
方法有 set reset。
set 回調(diào)函數(shù)觸發(fā)后調(diào)用 setState 更新 value。
reset 就是調(diào)用 set 并傳入 this.props.initial 即可。
2.2. ToggleToggle 是最直接利用 Value 即可實(shí)現(xiàn)的功能,因此放在 Value 之后說。Toggle 值是 boolean 類型,特別適合配合 Switch 等組件。
既然 Toggle 功能弱于 Value,為什么不用 Value 替代 Toggle 呢?這是個(gè)好問題,如果你不擔(dān)心自己代碼可讀性的話,的確可以永遠(yuǎn)不用 Toggle。用法
源碼{({ on, toggle }) => }
源碼地址
原料:Value
核心就是利用 Value 組件,value 重命名為 on,增加了 toggle 方法,繼承 set reset 方法:
export default { toggle: () => set(on => !on); }
理所因當(dāng),將 value 值限定在 boolean 范圍內(nèi)。
2.3. Counter與 Toggle 類似,這也是繼承了 Value 就可以實(shí)現(xiàn)的功能,計(jì)數(shù)器。
用法源碼{({ count, inc, dec }) => ( )}
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 count,增加了 inc dec incBy decBy 方法,繼承 set reset 方法。
與 Toggle 類似,Counter 將 value 限定在了數(shù)字,那么比如 inc 就會(huì)這么實(shí)現(xiàn):
export default { inc: () => set(value => value + 1); }
這里用到了 Value 組件 set 函數(shù)的多態(tài)用法。一般 set 的參數(shù)是一個(gè)值,但也可以是一個(gè)函數(shù),回調(diào)是當(dāng)前的值,這里返回一個(gè) +1 的新值。
2.4. List操作數(shù)組。
用法源碼{({ list, pull, push }) => (
)}{list.map({ tag }) => ( pull(value => value === tag)}> {tag} )}
源碼地址
原料:Value
依然利用 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 函數(shù)由 Mutable 修正為 Immutable,push pull 同理。
2.5. Set存儲(chǔ)數(shù)組對(duì)象,可以添加和刪除元素。類似 ES6 Set。和 List 相比少了許多功能函數(shù),因此只承擔(dān)添加、刪除元素的簡單功能。
用法需要注意的是,initial 是數(shù)組,而不是 Set 對(duì)象。
源碼{({ values, remove, add }) => ( )} {values.map(tag => ( remove(tag)}>{tag} ))}
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 values 且初始值為 [],增加了 add remove clear has 方法,保留 reset 方法。
實(shí)現(xiàn)依然很簡單,add remove clear 都利用 Value 提供的 set 進(jìn)行賦值,只要實(shí)現(xiàn)幾個(gè)操作數(shù)組方法即可:
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 方法則直接復(fù)用 hasItem。核心還是利用 Value 的 set 函數(shù)一招通吃,將操作目標(biāo)鎖定為數(shù)組類型罷了。
2.6. mapMap 的實(shí)現(xiàn)與 Set 很像,類似 ES6 的 Map。
用法與 Set 不同,Map 允許設(shè)置 Key 名。需要注意的是,initial 是對(duì)象,而不是 Map 對(duì)象。
源碼源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 values 且初始值為 {},增加了 set get clear has delete 方法,保留 reset 方法。
由于使用對(duì)象存儲(chǔ)數(shù)據(jù)結(jié)構(gòu),操作起來比數(shù)組方便太多,已經(jīng)不需要再解釋了。
值得吐槽的是,作者使用了 != 判斷 has:
export default { has: key => values[key] != null; }
這種代碼并不值得提倡,首先是不應(yīng)該使用二元運(yùn)算符,其次比較推薦寫成 values[key] !== undefined,畢竟 set("null", null) 也應(yīng)該算有值。
2.7. stateState 純粹為了替代 React setState 概念,其本質(zhì)就是換了名字的 Value 組件。
用法值得注意的是,setState 支持函數(shù)和值作為參數(shù),是 Value 組件本身支持的,State 組件額外適配了 setState 的另一個(gè)特性:合并對(duì)象。
{({ state, setState }) => { const onStart = data => setState({ loading: true }); const onFinish = data => setState({ data, loading: false }); return ( ); }}
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 state 且初始值為 {},增加了 setState 方法,保留 reset 方法。
setState 實(shí)現(xiàn)了合并對(duì)象的功能,也就是傳入一個(gè)對(duì)象,并不會(huì)覆蓋原始值,而是與原始值做 Merge:
export default { setState: (updater, cb) => set( prev => ({ ...prev, ...(typeof updater === "function" ? updater(prev) : updater) }), cb ); }2.8. Active
這是一個(gè)內(nèi)置鼠標(biāo)交互監(jiān)聽的容器,監(jiān)聽了 onMouseUp 與 onMouseDown,并依此判斷 active 狀態(tài)。
用法源碼{({ active, bind }) => ( You are {active ? "clicking" : "not clicking"} this div.)}
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 active 且初始值為 false,增加了 bind 方法。
bind 方法也巧妙利用了 Value 提供的 set 更新狀態(tài):
export default { bind: { onMouseDown: () => set(true), onMouseUp: () => set(false) } };2.9. Focus
與 Active 類似,F(xiàn)ocus 是當(dāng) focus 時(shí)才觸發(fā)狀態(tài)變化。
用法源碼{({ focused, bind }) => ( )}You are {focused ? "focusing" : "not focusing"} the input.
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 focused 且初始值為 false,增加了 bind 方法。
bind 方法與 Active 如出一轍,僅是監(jiān)聽時(shí)機(jī)變成了 onFocus 和 onBlur。
2.10. FocusManager不知道出于什么考慮,F(xiàn)ocusManager 的官方文檔是空的,而且 Help wanted。。
正如名字描述的,這是一個(gè) Focus 控制器,你可以直接調(diào)用 blur 來取消焦點(diǎn)。
用法筆者給了一個(gè)例子,在 5 秒后自動(dòng)失去焦點(diǎn):
源碼{({ focused, blur, bind }) => ( { setTimeout(() => { blur(); }, 5000); }} />)}You are {focused ? "focusing" : "not focusing"} the input.
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 focused 且初始值為 false,增加了 bind blur 方法。
blur 方法直接調(diào)用 document.activeElement.blur() 來觸發(fā)其 bind 監(jiān)聽的 onBlur 達(dá)到更新狀態(tài)的效果。
By the way, 還監(jiān)聽了 onMouseDown 與 onMouseUp:
export default { bind: { tabIndex: -1, onBlur: () => { if (canBlur) { set(false); } }, onFocus: () => set(true), onMouseDown: () => (canBlur = false), onMouseUp: () => (canBlur = true) } };
可能意圖是防止在 mouseDown 時(shí)觸發(fā) blur,因?yàn)?focus 的時(shí)機(jī)一般是 mouseDown。
2.11. Hover與 Focus 類似,只是觸發(fā)時(shí)機(jī)為 Hover。
用法源碼{({ hovered, bind }) => ( You are {hovered ? "hovering" : "not hovering"} this div.)}
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 hovered 且初始值為 false,增加了 bind 方法。
bind 方法與 Active、Focus 如出一轍,僅是監(jiān)聽時(shí)機(jī)變成了 onMouseEnter 和 onMouseLeave。
2.12. Touch與 Hover 類似,只是觸發(fā)時(shí)機(jī)為 Hover。
用法源碼{({ touched, bind }) => ( You are {touched ? "touching" : "not touching"} this div.)}
源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 touched 且初始值為 false,增加了 bind 方法。
bind 方法與 Active、Focus、Hover 如出一轍,僅是監(jiān)聽時(shí)機(jī)變成了 onTouchStart 和 onTouchEnd。
2.13. Field與 Value 組件唯一的區(qū)別,就是
用法這個(gè)用法和 Value 沒區(qū)別:
{({ value, set }) => ( set(e.target.value)} /> )}
但是用 bind 更簡單:
源碼{({ bind }) => }
源碼地址
原料:Value
依然利用 Value 組件,value 保留不變,初始值為 "",增加了 bind 方法,保留 set reset 方法。
與 Value 的唯一區(qū)別是,支持了 bind 并封裝 onChange 監(jiān)聽,與賦值受控屬性 value。
export default { bind: { value, onChange: event => { if (isObject(event) && isObject(event.target)) { set(event.target.value); } else { set(event); } } } };2.14. Form
這是一個(gè)表單工具,有點(diǎn)類似 Antd 的 Form 組件。
用法 )} 源碼源碼地址
原料:Value
依然利用 Value 組件,value 重命名為 values 且初始值為 {},增加了 setValues field 方法,保留 reset 方法。
表單最重要的就是 field 函數(shù),為表單的每一個(gè)控件做綁定,同時(shí)設(shè)置一個(gè)表單唯一 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); } } } }; } };
可以看到,為表單的每一項(xiàng)綁定的內(nèi)容與 Field 組件一樣,只是 Form 組件的行為是批量的。
2.15. IntervalInterval 比較有意思,將定時(shí)器以 JSX 方式提供出來,并且提供了 stop resume 方法。
用法源碼{({ start, stop }) => ( <> The time is now {new Date().toLocaleTimeString()}> )}
源碼地址
原料:無
提供了 start stop toggle 方法。
實(shí)現(xiàn)方式是,在組件內(nèi)部維護(hù)一個(gè) Interval 定時(shí)器,實(shí)現(xiàn)了組件更新、銷毀時(shí)的計(jì)時(shí)器更新、銷毀操作,可以認(rèn)為這種定時(shí)器的生命周期綁定了 React 組件的生命周期,不用擔(dān)心銷毀和更新的問題。
具體邏輯就不列舉了,利用 setInterval clearInterval 函數(shù)基本上就可以了。
2.16. ComposeCompose 也是個(gè)有趣的組件,可以將上面提到的任意多個(gè)組件組合使用。
用法源碼{(counter, toggle) => ( )}
源碼地址
原料:無
通過遞歸渲染出嵌套結(jié)構(gòu),并將每一層結(jié)構(gòu)輸出的值存儲(chǔ)到 propsList 中,最后一起傳遞給組件。這也是為什么每個(gè)函數(shù) value 一般都要重命名的原因。
在 精讀《Epitath 源碼 - renderProps 新用法》 文章中,筆者就介紹了利用 generator 解決高階組件嵌套的問題。
在 精讀《React Hooks》 文章中,介紹了 React Hooks 已經(jīng)實(shí)現(xiàn)了這個(gè)特性。
所以當(dāng)你了解了這三種 "compose" 方法后,就可以在合適的場(chǎng)景使用合適的 compose 方式簡化代碼。
3. 總結(jié)看完了源碼分析,不知道你是更感興趣使用這個(gè)庫呢,還是已經(jīng)躍躍欲試開始造輪子了呢?不論如何,這個(gè)庫的思想在日常的業(yè)務(wù)開發(fā)中都應(yīng)該大量實(shí)踐。
另外 Hooks 版的 PowerPlug 已經(jīng) 4 個(gè)月沒有更新了(非官方):react-powerhooks,也許下一個(gè)維護(hù)者/貢獻(xiàn)者 就是你。
討論地址是:精讀《React PowerPlug》 · Issue #129 · dt-fe/weekly
如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀 - 幫你篩選靠譜的內(nèi)容。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102068.html
摘要:精讀源碼一共行,我們分析一下其精妙的方式。更多討論討論地址是精讀新用法如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。 1 引言 很高興這一期的話題是由 epitath 的作者 grsabreu 提供的。 前端發(fā)展了 20 多年,隨著發(fā)展中國家越來越多的互聯(lián)網(wǎng)從業(yè)者涌入,現(xiàn)在前端知識(shí)玲瑯滿足,概念、庫也越來越多。雖然內(nèi)容越來越多,但作為個(gè)體的...
摘要:精讀原文介紹了學(xué)習(xí)源碼的兩個(gè)技巧,并利用實(shí)例說明了源碼學(xué)習(xí)過程中可以學(xué)到許多周邊知識(shí),都讓我們受益匪淺。討論地址是精讀源碼學(xué)習(xí)如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。 1. 引言 javascript-knowledge-reading-source-code 這篇文章介紹了閱讀源碼的重要性,精讀系列也已有八期源碼系列文章,分別是: 精讀《Immer.js》源...
摘要:可以看到,這樣不僅沒有占用組件自己的,也不需要手寫回調(diào)函數(shù)進(jìn)行處理,這些處理都?jí)嚎s成了一行。效果通過拿到周期才執(zhí)行的回調(diào)函數(shù)。實(shí)現(xiàn)等價(jià)于的回調(diào)僅執(zhí)行一次時(shí),因此直接把回調(diào)函數(shù)拋出來即可。 1 引言 上周的 精讀《React Hooks》 已經(jīng)實(shí)現(xiàn)了對(duì) React Hooks 的基本認(rèn)知,也許你也看了 React Hooks 基本實(shí)現(xiàn)剖析(就是數(shù)組),但理解實(shí)現(xiàn)原理就可以用好了嗎?學(xué)的是...
摘要:會(huì)自動(dòng)觸發(fā)函數(shù)內(nèi)回調(diào)函數(shù)的執(zhí)行。因此利用并將依賴置為使代碼在所有渲染周期內(nèi),只在初始化執(zhí)行一次。同時(shí)代碼里還對(duì)等公共方法進(jìn)行了包裝,讓這些回調(diào)函數(shù)中自帶效果。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 react-easy-state 是個(gè)比較有趣的庫,利用 Proxy 創(chuàng)建了一個(gè)非常易用的全局?jǐn)?shù)據(jù)流管理方式。 import React from react; import { stor...
showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢? 優(yōu)質(zhì)gitHub開源練手項(xiàng)目: ...
閱讀 2019·2021-08-21 14:09
閱讀 484·2019-08-30 15:44
閱讀 2111·2019-08-29 16:32
閱讀 1374·2019-08-29 15:36
閱讀 3441·2019-08-29 12:43
閱讀 2784·2019-08-29 11:14
閱讀 434·2019-08-28 18:26
閱讀 2251·2019-08-26 13:57