摘要:另外一點(diǎn)是組件應(yīng)該盡量保證獨(dú)立性,避免和外部的耦合,使用全局事件造成了和外部事件的耦合。明確的職責(zé)分配也增加了應(yīng)用的確定性明確只有組件能夠知道狀態(tài)數(shù)據(jù),且是對(duì)應(yīng)部分的數(shù)據(jù)。
4.2 react patterns書籍完整目錄
修改 Props
Immutable data representation
確定性
在 getInitialState 中使用 props
私有狀態(tài)和全局事件
render 包含 side effects
jQuery 修改 DOM
使用無狀態(tài)組件
內(nèi)存管理
componentWillUnmount 取消訂閱事件
判斷 isMounted
上層設(shè)計(jì)
使用 container component
使用 Composition 替代 mixins
Composability - Presenter Pattern
Composability - Decorator Pattern
Context 數(shù)據(jù)傳遞
4.2.1 關(guān)于React 的框架設(shè)計(jì)是趨于函數(shù)式的,其中最主要的兩點(diǎn)也是為什么會(huì)選擇 React 的兩點(diǎn):
單向性:數(shù)據(jù)的流動(dòng)是單向的
確定性:React(storeData) = view 相同數(shù)據(jù)總是渲染出相同的 view
這兩點(diǎn)即是特性也是設(shè)計(jì) React 應(yīng)用的基本原則,圍繞這兩個(gè)原則社區(qū)里邊出現(xiàn)了一些 React 設(shè)計(jì)模式,即有好的設(shè)計(jì)模式也有應(yīng)該要避免的反模式,理解這些設(shè)計(jì)模式能夠幫助我們寫出更優(yōu)質(zhì)的 React 應(yīng)用,本節(jié)將圍繞 單向性、確定性、內(nèi)存管理、上層設(shè)計(jì) 來討論這些設(shè)計(jì)模式。
4.2.2 單向性anti 表示反模式,good 表示好模式
數(shù)據(jù)的流動(dòng)是單向的
修改 Props (anti)描述: 組件任何地方修改 props 的值
解釋:
React 的數(shù)據(jù)流動(dòng)是單向性的,流動(dòng)的方式是通過 props 傳遞到組件中,而在 Javascript 中對(duì)象是通過引用傳遞的,修改 props 等于直接修改了 store 中的數(shù)據(jù),導(dǎo)致破壞數(shù)據(jù)的單向流動(dòng)特性
使用不可變數(shù)據(jù) (good)描述: store data 使用不可變數(shù)據(jù)
解釋: Javascript 對(duì)象的特性是可以任意修改,而這個(gè)特性很容易破壞數(shù)據(jù)的單向性,因?yàn)槿斯o法永遠(yuǎn)確保數(shù)據(jù)沒有被修改過,唯一的做法是使用不可變數(shù)據(jù),用代碼邏輯確保數(shù)據(jù)不能被任意修改,后面會(huì)有一個(gè)完整的小節(jié)介紹不可變數(shù)據(jù)在 React 中的應(yīng)用
4.2.3 確定性React(storeData) = view 相同數(shù)據(jù)總是渲染出相同的 view
在 getInitialState 中使用 props (anti)描述: getInitialState 通過 props 來生成 state 數(shù)據(jù)
解釋:
官方文檔 https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
在 getInitialState 中通過 props 來計(jì)算 state 破壞了確定性原則,“source of truth” 應(yīng)該只是來自于一個(gè)地方,通過計(jì)算 state 過后增加了 truth source。這種做法的另外一個(gè)壞處是在組件更新的時(shí)候,還需要計(jì)算重新計(jì)算這部分 state。
舉例:
var MessageBox = React.createClass({ getInitialState: function() { return {nameWithQualifier: "Mr. " + this.props.name}; }, render: function() { return{this.state.nameWithQualifier}; } }); ReactDOM.render(, mountNode);
優(yōu)化方式:
var MessageBox = React.createClass({ render: function() { return{"Mr. " + this.props.name}; } }); ReactDOM.render(, mountNode);
需要注意的是以下這種做法并不會(huì)影響確定性
var Counter = React.createClass({ getInitialState: function() { // naming it initialX clearly indicates that the only purpose // of the passed down prop is to initialize something internally return {count: this.props.initialCount}; }, handleClick: function() { this.setState({count: this.state.count + 1}); }, render: function() { return私有狀態(tài)和全局事件 (anti){this.state.count}; } }); ReactDOM.render(, mountNode);
描述: 在組件中定義私有的狀態(tài)或者使用全局事件
介紹: 組件中定義了私有狀態(tài)和全局事件過后,組件的渲染可能會(huì)出現(xiàn)不一致,因?yàn)槿质录退接袪顟B(tài)都可以控制組件的狀態(tài),這樣外部使用組件無法保證組件的渲染結(jié)果,影響了組件的確定性。另外一點(diǎn)是組件應(yīng)該盡量保證獨(dú)立性,避免和外部的耦合,使用全局事件造成了和外部事件的耦合。
render 函數(shù)包含 side effects (anti)side effect 解釋: https://en.wikipedia.org/wiki/Side_effect_(computer_science)
描述: render 函數(shù)包含一些 side effects 的代碼邏輯,這些邏輯包括如
修改 state 數(shù)據(jù)
修改 props 數(shù)據(jù)
修改全局變量
調(diào)用其他導(dǎo)致 side effect 的函數(shù)
解釋: render 函數(shù)如果包含了 side effect ,渲染的結(jié)果不再可信,所以確保 render 函數(shù)為純函數(shù)
jQuery 修改 DOM (anti)描述: 使用外部 DOM 框架修改或刪除了 DOM 節(jié)點(diǎn)、屬性、樣式
解釋: React 中 DOM 的結(jié)構(gòu)和屬性都是由渲染函數(shù)確定的,如果使用了 Jquery 修改 DOM,那么可能造成沖突,視圖的修改源頭增加,直接影響組件的確定性
描述: 優(yōu)先使用無狀態(tài)組件
解釋: 無狀態(tài)組件更符合函數(shù)式的特性,如果組件不需要額外的控制,只是渲染結(jié)構(gòu),那么應(yīng)該優(yōu)先選擇無狀態(tài)組件
描述: 如果組件需要注冊(cè)訂閱事件,可以在 componentDidMount 中注冊(cè),且必須在 ComponentWillUnmount 中取消訂閱
解釋: 在組件 unmount 后如果沒有取消訂閱事件,訂閱事件可能仍然擁有組件實(shí)例的引用,這樣第一是組件內(nèi)存無法釋放,第二是引起不必要的錯(cuò)誤
描述: 在組件中使用 isMounted 方法判斷組件是否未被注銷
解釋:
React 中在一個(gè)組件 ummount 過后使用 setState 會(huì)出現(xiàn)warning提示(通常出現(xiàn)在一些事件注冊(cè)回調(diào)函數(shù)中) ,避免 warning 的解決辦法是:
if(this.isMounted()) { // This is bad. this.setState({...}); }
但這是個(gè)掩耳盜鈴的做法,因?yàn)槿绻霈F(xiàn)了錯(cuò)誤提示就表示在組件 unmount 的時(shí)候還有組件的引用,這個(gè)時(shí)候應(yīng)該是已經(jīng)導(dǎo)致了內(nèi)存溢出。所以解決錯(cuò)誤的正確方法是在 componentWillUnmount 函數(shù)中取消監(jiān)聽:
class MyComponent extends React.Component { componentDidMount() { mydatastore.subscribe(this); } render() { ... } componentWillUnmount() { mydatastore.unsubscribe(this); } }4.2.5 上層設(shè)計(jì) 使用 container component (good)
描述: 將 React 組件分為兩類 container 、normal ,container 組件負(fù)責(zé)獲取狀態(tài)數(shù)據(jù),然后傳遞給與之對(duì)應(yīng)的 normal component,對(duì)應(yīng)表示兩個(gè)組件的名稱對(duì)應(yīng),舉例:
TodoListContainer => TodoList FooterContainer => Footer
解釋: 參看 redux 設(shè)計(jì)中的 container 組件,container 組件是 smart 組件,normal 組件是 dummy 組件,這樣的責(zé)任分離讓 normal 組件更加獨(dú)立,不需要知道狀態(tài)數(shù)據(jù)。明確的職責(zé)分配也增加了應(yīng)用的確定性(明確只有 container 組件能夠知道狀態(tài)數(shù)據(jù),且是對(duì)應(yīng)部分的數(shù)據(jù))。
使用 Composition 替代 mixins (good)描述: 使用組件的組合的方式(高階組件)替代 mixins 實(shí)現(xiàn)為組件增加附加功能
解釋:
mixins 的設(shè)計(jì)主要目的是給組件提供插件機(jī)制,大多數(shù)情況使用 mixin 是為了給組件增加額外的狀態(tài)。但是使用 mixins 會(huì)帶來一些額外的壞處:
mixins 通常需要依賴組件定義特定的方法,如 getSomeMixinState ,而這個(gè)是隱式的約束
多個(gè) mixins 可能會(huì)導(dǎo)致沖突
mixins 通常增加了額外的狀態(tài)數(shù)據(jù),而 react 的設(shè)計(jì)應(yīng)該是要避免過多的內(nèi)部狀態(tài)
mixins 可能會(huì)影響 shouldComponentUpdate 的邏輯, mixins 做了很多數(shù)據(jù)合并的邏輯
另外一點(diǎn)是在新版本的 React 中,mixins 將會(huì)是廢棄的 feature,在 es6 class 定義組件也不會(huì)支持 mixins。
舉個(gè)例子,一個(gè)訂閱 fluxstore 的 mixin 為:
function StoreMixin(store) { var Mixin = { getInitialState() { return this.getStateFromStore(this.props); }, componentDidMount() { store.addChangeListener(this.handleStoreChanged) this.setState(this.getStateFromStore(this.props)); }, componentWillUnmount() { store.removeChangeListener(this.handleStoreChanged) }, handleStoreChanged() { if (this.isMounted()) { this.setState(this.getStateFromStore(this.props)); } } }; return Mixin; }
使用
const TodolistContainer = React.createClass({ mixins: [StoreMixin(AppStore)], getStateFromStore(props) { return { todos: AppStore.get("todos"); } } })
轉(zhuǎn)換為組件的組合方式為:
function connectToStores(Component, store, getStateFromStore) { const StoreConnection = React.createClass({ getInitialState() { return getStateFromStore(this.props); }, componentDidMount() { store.addChangeListener(this.handleStoreChanged) }, componentWillUnmount() { store.removeChangeListener(this.handleStoreChanged) }, handleStoreChanged() { if (this.isMounted()) { this.setState(getStateFromStore(this.props)); } }, render() { return; } }); return StoreConnection; };
使用方式:
class Todolist extends React.Component { render() { // .... } } TodolistContainer = connectToStore(Todolist, AppStore, props => { todos: AppStore.get("todos") })Presenter Pattern
描述: 利用 children 可以作為函數(shù)的特性,將數(shù)據(jù)獲取和數(shù)據(jù)表現(xiàn)分離成為兩個(gè)不同的組件
如下例子:
class DataGetter extends React.Component { render() { const { children } = this.props const data = [ 1,2,3,4,5 ] return children(data) } } class DataPresenter extends React.Component { render() { return ({data => ) } } const App = React.createClass({ render() { return ({data.map((datum) => (
}- {datum}
))}) } })
解釋: 將數(shù)據(jù)獲取和數(shù)據(jù)展現(xiàn)分離,同時(shí)利用組件的 children 可以作為函數(shù)的特性,讓數(shù)據(jù)獲取和數(shù)據(jù)展現(xiàn)都可以作為組件使用
Decorator Pattern描述: 父組件通過 cloneElement 方法給子組件添加方法和屬性
cloneElement 方法:
ReactElement cloneElement( ReactElement element, [object props], [children ...] )
如下例子:
const CleverParent = React.createClass({ render() { const children = React.Children.map(this.props.children, (child) => { return React.cloneElement(child, { // 新增 onClick 屬性 onClick: () => alert(JSON.stringify(child.props, 0, 2)) }) }) return{children}} }) const SimpleChild = React.createClass({ render() { return ({this.props.children}) } }) const App = React.createClass({ render() { return () } }) 1 2
解釋: 通過這種設(shè)計(jì)模式,可以應(yīng)用到一些自定義的組件設(shè)計(jì),提供更簡(jiǎn)潔的 API 給第三方使用,如 facebook 的 FixedDataTable 也是應(yīng)用了這種設(shè)計(jì)模式
Context 數(shù)據(jù)傳遞描述: 通過 Context 可以讓所有組件共享相同的上下文,避免數(shù)據(jù)的逐級(jí)傳遞, Context 是大多數(shù) flux 庫(kù)共享 store 的基本方法。
使用方法:
/** * 初始化定義 Context 的組件 */ class Chan extends React.Component { getChildContext() { return { environment: "grandma"s house" } } } // 設(shè)置 context 類型 Chan.childContextTypes = { environment: React.PropTypes.string }; /** * 子組件獲取 context */ class ChildChan extends React.Component { render() { const ev = this.context.environment; } } /** * 需要設(shè)置 contextTypes 才能獲取 */ ChildChan.contextTypes = { environment: React.PropTypes.string };
解釋: 通常情況下 Context 是為基礎(chǔ)組件提供的功能,一般情況應(yīng)該避免使用,否則濫用 Context 會(huì)影響應(yīng)用的確定性。
參考鏈接https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.hvbsii4zd
http://www.zhubert.com/blog/2016/02/05/react-composability-patterns/
https://medium.com/@learnreact/context-f932a9abab0e#.wn00ktlde
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/79819.html
書籍完整目錄 4.1 react 代碼規(guī)范 showImg(https://segmentfault.com/img/bVyE9m); 關(guān)于 基礎(chǔ)規(guī)范 組件結(jié)構(gòu) 命名規(guī)范 jsx 書寫規(guī)范 eslint-plugin-react 關(guān)于 在代碼的設(shè)計(jì)上,每個(gè)團(tuán)隊(duì)可能都有一定的代碼規(guī)范和模式,好的代碼規(guī)范能夠提高代碼的可讀性便于協(xié)作溝通,好的模式能夠上層設(shè)計(jì)上避免不必要的 bug 出現(xiàn)。本節(jié)會(huì)參考...
摘要:需要提醒讀者的是,的很多例子都是通過來寫的,但這并不是語(yǔ)法,后面我們會(huì)有單獨(dú)的一小節(jié)講解的基本語(yǔ)法,不過目前為止我們先將跟多精力放在上。 書籍完整目錄 1.2 JSX 語(yǔ)法 showImg(https://segmentfault.com/img/bVvKLR); 官方文檔 https://facebook.github.io/react/docs/jsx-in-depth.html ...
摘要:?jiǎn)蜗驍?shù)據(jù)流應(yīng)用的核心設(shè)計(jì)模式,數(shù)據(jù)流向自頂向下我也是性子急的人,按照技術(shù)界的慣例,在學(xué)習(xí)一個(gè)技術(shù)前,首先得說一句。然而的單向數(shù)據(jù)流的設(shè)計(jì)讓前端定位變得簡(jiǎn)單,頁(yè)面的和數(shù)據(jù)的對(duì)應(yīng)是唯一的我們可以通過定位數(shù)據(jù)變化就可以定位頁(yè)面展現(xiàn)問題。 書籍完整目錄 1.1 React 介紹 showImg(https://segmentfault.com/img/bVvJgS); 1.1.1 React ...
閱讀 1905·2021-11-24 11:16
閱讀 3260·2021-09-10 10:51
閱讀 3199·2021-08-03 14:03
閱讀 1266·2019-08-29 17:03
閱讀 3244·2019-08-29 12:36
閱讀 2232·2019-08-26 14:06
閱讀 497·2019-08-23 16:32
閱讀 2677·2019-08-23 13:42