1. 程式人生 > >React從零學起

React從零學起

原文請戳

初接觸React,除了不習慣其元件化的設計原則外,往往它所‘依賴’的眾多module也會讓初學者感到困惑,使得不知從何學起。此文只是我對React的一些淺析,希望能幫助想要學習React的朋友入門。

1.React從來就是獨立的

正如上面我提到的,React’依賴’了很多module,但是這種依賴並不是所謂的耦合依賴,只是為了更好的去實現React。換句話說,React只是一個View層面的框架,它可以和其他module自然的融合(更好的去實現)。

我們可以只利用React去實現官網上那個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,它的內部實現了計數的演算法以及state的管理機制,這個Component的例項就是計數器,該計數器也很簡單,可以實現增加和減少。

最後是 index.js

var React = require('react');
var ReactDOM = require('react-dom');
var MyCounter = require('./components/MyCounter');

ReactDOM.render(
    <MyCounter />,
    document.getElementById('root')
);

到此,我們就利用React實現了一個小小的計數器,儘管它很簡單,但是卻未使用任何其他module,所以我在上面提到,React本身就是獨立的。

所以,引用其他module,只是為了實現更復雜的React App,使得其更具有擴充套件性。

2.Container

那麼問題來了,如果我還像要一個類似的Component,但是每次計數的時候不是加1或者減1,而是乘2或除2,那怎麼做呢?

你可別告訴我重新一個類似上面MyCounter的Component,然後繫結不同的事件,那如果是這樣的話React也太low了吧,這還叫什麼元件化呢,元件化最基本的特點就是複用啊。

所以React期望我們這麼做:

對於任何Compoent,儘量將其作為靜態展示的Component,即其只負責展示UI,然後在它的外層巢狀一個Container, Container中定義了該Compoent所需要的引數以及方法,這樣,當我們需要複用Component時,UI已經是現成的了,而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與邏輯分離成功,是不是感覺瞬間清爽許多。
關於什麼時Contianer Component和Presentation Component,推薦此文

3.Use store to help dispatch actions

分離了UI後,的確邏輯上清楚了許多,但仔細觀察會發現,上面的 MyCounterContainer 狀態的改變只是兩個button。而React認為Component的狀態變化必定是由一個行為,即action造成的,因此,我們需要將上面的加減法抽象為一個行為驅動的事件,即一個行為對應一種狀態結果。而 redux 就是幹這事兒的,它通過createStore去建立一個store,這個store可以管理和知曉這個Component的狀態,它通過dispatch分發action然後得到最新的狀態結果。

利用store,我們將 MyCounterContainer 重構如下:

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

仔細觀察上次重構,不難發現還是有些問題:

其一,儘管利用store幫我們管理了state,但是還是得我們手動setState,太過耦合。

其二,對於傳遞給子Component的引數,還是寫死在Container裡,不具有封裝性和靈活性。

為了解決這個問題,我們可以利用 react-redux 的connect來解決。
connect可以把自定義的state和dispatch分發事件繫結到Component上,其中mapStateToProps正如其名,可以將state作為Component的props傳遞下去;而mapDispatchToProps則可以把action觸發邏輯傳遞下去,這樣我們可以很靈活的傳遞功能事件了。

利用connect我們繼續重構,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

上面的例子已經很接近React的初級App的設計了,但當我們的Component特別複雜時,往往action也會難抽象,像上面的dispatch({type: “PLUS”, value: 2});偶合度太高,因為我們根本不知道這個action為什麼是這樣,就好比我們隨便寫了一個常量而並未定義任何變數名一樣,別人是很難閱讀的。因此比較好的做法是把action更小的分離,比如上面的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.寫在最後

個人認為,學習React十分不推薦一上手就用各種module,或者照貓畫虎式的去填空,這樣只能是到頭來什麼也不會。當你從頭開始去理解時,才能找到痛點,而當你有痛點時你才需要重構,那麼此時可能某個module就是你想要的。你用它只是為了省時,而不是你做不出來才用它。借用我前幾天在知乎上回答的問題“用庫丟臉不?”,我的觀點是:用庫不丟臉,不懂庫還非要用庫才丟臉原文請戳