React框架將資料流的概念帶入前端。資料流向各個components,因此在components中控管資料有兩個管道,一個是props另一個則是state,React根據這兩個物件的變化來動態的渲染DOM。React有個重要的規則,就是資料的傳遞是單向的,僅能由Parents components傳遞給Child components,會有這個規則是因為React撰寫時希望降低components之間的耦合度,如果資料能由Child傳遞給Parents,這樣子會造成Parents依賴于Child,導致耦合度提高。但是因為單項資料流的關係,常常在子孫代的components取得資料必須長途跋涉,因此產生了Redux、MobX,這種全域的State機制,既然有了Redux來幫我們管理State,那我們是否需要完全棄用React原生的state呢?這篇分享一下我在使用上的經驗,以及解答我何時使用原生的state何時使用Redux。
React State
原生的state機制使用起來十分的簡單,對於剛開始學習React的使用者來說是非常好用的工具,很容易就可以利用state做到一些華麗的功能,例如數據綁定等等。但是當業務的邏輯開始複雜,像是導入Router或者需要從API Server fetch資料時就會讓一切變得糟糕,因為原生的state在傳遞資料時僅能向下傳遞,如果我們需要在父代與子代組件間共用數據,那我們就必須在一個更高位的component來存放state,而另一件麻煩的事是當子代需要有能力控制祖先傳下來的state時,祖先們必須要把控制state的callback用props的方式傳遞下來給他。
// Parents Component
class Parents extends Component {
constructor() {
super();
this.state = {
count: 0
};
this.changeCount = this.changeCount.bind(this);
}
changeCount(num) {
this.setState({...this.state, count: num});
}
render() {
return (
<div className="App">
<h1>Parents</h1>
<p>Count: {this.state.count}</p>
<hr/>
<Child count={this.state.count} changeCount={this.changeCount}/>
</div>
);
}
}
可以發現在19行的時候,我們將parents的state以及控制這個state的方法都用props的方式傳遞給child。
// Child Component
class Child extends Component {
render() {
return (
<div>
<h1>Child</h1>
<input type="input" value={this.props.count} onChange={(e) => this.props.changeCount(e.target.value)} />
</div>
)
}
}
export default Child;
而在child我們渲染props中的內容,並且使用props傳遞過來的方法來進行數據修改。
這個例子Parents Component與Child Component僅存在著一層的關係,可是未來我們可能產生資料傳遞了8層以上的情形產生,傳遞過程中可能大部分的component都用不到這個state,傳遞這麼大量的state以及callback就顯得笨重,而且trace code的難度也會變高很多。
Redux
Redux由action、reducer、store三個部分組成。Redux在React之外扮演著狀態機的角色,React原件可以透過綁定的方式取得這個狀態機上的資料,而實務上會將資料綁定在container components上,它是一個不需要渲染的組件,專門用來接收redux的資料,而再經由props的機制將資料傳遞給child。Redux比起原生state,能夠依照需求在需要資料的component前再建立container components,減少資料傳遞的層數,加上reducer為pure function的關係,我們可以很容易的透過Redux的開發工具觀察資料的變化,甚至做到時光回溯的效果。
我認為Redux的學習成本比起原生state算是高很多的,使用redux前必須要懂action的形式,reduce需要注意的事項,store的建立,以及如何將資料綁定在container components上。React本身的學習曲線就比較高,可能會一下子有太多的新知識湧入,而難以應用。
使用場景
這邊我彙整一下自己使用的經驗,以及遇到什麼樣的場景會用使用原生state,什麼情況會使用Redux。
原生State
- 當這個狀態只作用於component本身時,就沒有必要將他存到redux上
- 開發小型應用,業務邏輯少時
- 開發獨立的component,為了減少依賴,只使用原生State
Redux
- 當有複雜的UI狀態變化,如不同router頁面間的互動
- 資料具有長期的使用性,例如fetch回來的資料,我們可能會在回來又需要這些資料,存在redux可以隨時存取
- 資料由多個組件控制,或者變化十分快速,我們希望透過redux的開發工具來幫助我們理解來龍去脈