初接觸React嗜憔,除了不習(xí)慣其組件化的設(shè)計原則外杜跷,往往它所‘依賴’的眾多module也會讓初學(xué)者感到困惑,使得不知從何學(xué)起歪架。此文只是我對React的一些淺析股冗,希望能幫助想要學(xué)習(xí)React的朋友入門。
1.React從來就是獨(dú)立的
正如上面我提到的和蚪,React'依賴'了很多module止状,但是這種依賴并不是所謂的耦合依賴烹棉,只是為了更好的去實現(xiàn)React。換句話說怯疤,React只是一個View層面的框架峦耘,它可以和其他module自然的融合(更好的去實現(xiàn))。
我們可以只利用React去實現(xiàn)官網(wǎng)上那個Counter的demo旅薄,這里我做了一個簡易版辅髓。
index.html
<!DOCTYPE html>
<html>
<head>
<title>Redux counter example</title>
</head>
<body>
<div id="root">
</div>
<script src="/static/bundle.js"></script>
</body>
</html>
頁面只引了一個js文件,該文件為webpack打包而成少梁,具體webpack打包原理不在這里贅述洛口。
MyCounter.js
var React = require('react');
var Counter = React.createClass({
getInitialState: function() {
return {value: 0};
},
plus: function() {
this.setState({
value: ++this.state.value
});
},
minus: function() {
this.setState({
value: --this.state.value
});
},
render: function() {
return (
<div>
<button onClick={this.plus}>+</button>
<span>{this.state.value}</span>
<button onClick={this.minus}>-</button>
</div>
);
}
});
module.exports = Counter;
這是典型的React的Component,它的內(nèi)部實現(xiàn)了計數(shù)的算法以及state的管理機(jī)制凯沪,這個Component的實例就是計數(shù)器第焰,該計數(shù)器也很簡單,可以實現(xiàn)增加和減少妨马。
最后是 index.js
var React = require('react');
var ReactDOM = require('react-dom');
var MyCounter = require('./components/MyCounter');
ReactDOM.render(
<MyCounter />,
document.getElementById('root')
);
到此挺举,我們就利用React實現(xiàn)了一個小小的計數(shù)器,盡管它很簡單烘跺,但是卻未使用任何其他module湘纵,所以我在上面提到,React本身就是獨(dú)立的滤淳。
所以梧喷,引用其他module,只是為了實現(xiàn)更復(fù)雜的React App脖咐,使得其更具有擴(kuò)展性铺敌。
2.Container
那么問題來了,如果我還像要一個類似的Component屁擅,但是每次計數(shù)的時候不是加1或者減1偿凭,而是乘2或除2,那怎么做呢派歌?
你可別告訴我重新一個類似上面MyCounter的Component弯囊,然后綁定不同的事件,那如果是這樣的話React也太low了吧硝皂,這還叫什么組件化呢常挚,組件化最基本的特點(diǎn)就是復(fù)用啊。
所以React期望我們這么做:
對于任何Compoent稽物,盡量將其作為靜態(tài)展示的Component,即其只負(fù)責(zé)展示UI折欠,然后在它的外層嵌套一個Container贝或, Container中定義了該Compoent所需要的參數(shù)以及方法吼过,這樣,當(dāng)我們需要復(fù)用Component時咪奖,UI已經(jīng)是現(xiàn)成的了盗忱,而Container中的邏輯部分也可以共享,換個“殼子”就是一個具有其他功能的Compoent了羊赵。
于是趟佃,分解如下:
MyCounterContainer.js
var React = require('react');
var Counter = require('../components/MyCounter');
var CounterContainer = React.createClass({
getInitialState: function() {
return {
value: this.props.value
};
},
plus: function() {
this.setState({
value: this.state.value + 1
});
},
minus: function() {
this.setState({
value: this.state.value - 1
});
},
render: function() {
return (
<Counter plus={this.plus}
minus={this.minus}
value={this.state.value} />
);
}
});
module.exports = CounterContainer;
MyCounter
var React = require('react');
var Counter = React.createClass({
render: function() {
return (
<div>
<span>{this.props.value}</span>
<button onClick={this.props.plus}>+</button>
<button onClick={this.props.minus}>-</button>
</div>
);
}
});
module.exports = Counter;
index.js
var React = require('react');
var ReactDOM = require('react-dom');
var MyCounterContainer = require('./container/MyCounterContainer');
ReactDOM.render(
<MyCounterContainer value={0} />,
document.getElementById('root')
);
UI與邏輯分離成功,是不是感覺瞬間清爽許多昧捷。
關(guān)于什么時Contianer Component和Presentation Component闲昭,推薦此文。
3.Use store to help dispatch actions
分離了UI后靡挥,的確邏輯上清楚了許多序矩,但仔細(xì)觀察會發(fā)現(xiàn),上面的 MyCounterContainer 狀態(tài)的改變只是兩個button跋破。而React認(rèn)為Component的狀態(tài)變化必定是由一個行為簸淀,即action造成的,因此毒返,我們需要將上面的加減法抽象為一個行為驅(qū)動的事件租幕,即一個行為對應(yīng)一種狀態(tài)結(jié)果。而 redux 就是干這事兒的拧簸,它通過createStore去創(chuàng)建一個store令蛉,這個store可以管理和知曉這個Component的狀態(tài),它通過dispatch分發(fā)action然后得到最新的狀態(tài)結(jié)果狡恬。
利用store珠叔,我們將 MyCounterContainer 重構(gòu)如下:
var React = require('react');
var Counter = require('../components/MyCounter');
var createStore = require('redux').createStore;
var counter = function(state, action) {
switch (action.type) {
case 'PLUS':
return state + action.value;
case 'MINUS':
return state - action.value;
default:
return state;
}
};
var store = createStore(counter, 1000);
var CounterContainer = React.createClass({
plus: function() {
var nextState = store.dispatch({
type: "PLUS",
value: 2
});
this.setState(nextState);
},
minus: function() {
var nextState = store.dispatch({
type: "MINUS",
value: 2
});
this.setState(nextState);
},
render: function() {
return (
<Counter plus={this.plus}
minus={this.minus}
value={store.getState()} />
);
}
});
module.exports = CounterContainer;
4.Use connect to manage the dispatch and reducer
仔細(xì)觀察上次重構(gòu),不難發(fā)現(xiàn)還是有些問題:
其一弟劲,盡管利用store幫我們管理了state祷安,但是還是得我們手動setState,太過耦合兔乞。
其二汇鞭,對于傳遞給子Component的參數(shù),還是寫死在Container里庸追,不具有封裝性和靈活性霍骄。
為了解決這個問題,我們可以利用 react-redux 的connect來解決淡溯。
connect可以把自定義的state和dispatch分發(fā)事件綁定到Component上读整,其中mapStateToProps正如其名,可以將state作為Component的props傳遞下去咱娶;而mapDispatchToProps則可以把a(bǔ)ction觸發(fā)邏輯傳遞下去米间,這樣我們可以很靈活的傳遞功能事件了强品。
利用connect我們繼續(xù)重構(gòu),MyCounterContainer 如下:
var React = require('react');
var Counter = require('../components/MyCounter');
var createStore = require('redux').createStore;
var connect = require('react-redux').connect;
var mapStateToProps = function(state) {
return {
value: state
}
};
var mapDispatchToProps = function(dispatch) {
return {
plus: function() {
dispatch({
type: "PLUS",
value: 2
});
},
minus: function() {
dispatch({
type: "MINUS",
value: 2
});
}
}
};
var CounterContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
module.exports = CounterContainer;
5.Split actions
上面的例子已經(jīng)很接近React的初級App的設(shè)計了屈糊,但當(dāng)我們的Component特別復(fù)雜時的榛,往往action也會難抽象,像上面的dispatch({type: "PLUS", value: 2});偶合度太高逻锐,因為我們根本不知道這個action為什么是這樣夫晌,就好比我們隨便寫了一個常量而并未定義任何變量名一樣,別人是很難閱讀的昧诱。因此比較好的做法是把a(bǔ)ction更小的分離晓淀,比如上面的action,我們可以分離成如下:
module.exports.plusAction = function(val) {
return {
type: "PLUS",
value: val
};
}
module.exports.minusAction = function(val) {
return {
type: "MINUS",
value: val
};
}
這樣鳄哭,在dispatch時要糊,也會顯得很簡潔。
6.ES6 refactor
ES6部分就不在這里贅述了妆丘,大多都是語法問題锄俄,建議大家可以參考阮老師的書ES6入門
7.寫在最后
個人認(rèn)為,學(xué)習(xí)React十分不推薦一上手就用各種module勺拣,或者照貓畫虎式的去填空奶赠,這樣只能是到頭來什么也不會。當(dāng)你從頭開始去理解時药有,才能找到痛點(diǎn)毅戈,而當(dāng)你有痛點(diǎn)時你才需要重構(gòu),那么此時可能某個module就是你想要的愤惰。你用它只是為了省時苇经,而不是你做不出來才用它。借用我前幾天在知乎上回答的問題“用庫丟臉不宦言?”扇单,我的觀點(diǎn)是:用庫不丟臉,不懂庫還非要用庫才丟臉奠旺。 原文請戳