React從零學(xué)起

原文請戳

初接觸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)是:用庫不丟臉,不懂庫還非要用庫才丟臉奠旺。 原文請戳

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜘澜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子响疚,更是在濱河造成了極大的恐慌鄙信,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忿晕,死亡現(xiàn)場離奇詭異装诡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門慎王,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚓土,“玉大人宏侍,你說我怎么就攤上這事赖淤。” “怎么了谅河?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵咱旱,是天一觀的道長。 經(jīng)常有香客問我绷耍,道長吐限,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任褂始,我火速辦了婚禮诸典,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崎苗。我一直安慰自己狐粱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布胆数。 她就那樣靜靜地躺著肌蜻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪必尼。 梳的紋絲不亂的頭發(fā)上蒋搜,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音判莉,去河邊找鬼豆挽。 笑死,一個胖子當(dāng)著我的面吹牛券盅,可吹牛的內(nèi)容都是我干的帮哈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渗饮,長吁一口氣:“原來是場噩夢啊……” “哼但汞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起互站,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤私蕾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胡桃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踩叭,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了容贝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片自脯。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖斤富,靈堂內(nèi)的尸體忽然破棺而出膏潮,到底是詐尸還是另有隱情,我是刑警寧澤满力,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布焕参,位于F島的核電站,受9級特大地震影響油额,放射性物質(zhì)發(fā)生泄漏叠纷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一潦嘶、第九天 我趴在偏房一處隱蔽的房頂上張望涩嚣。 院中可真熱鬧,春花似錦掂僵、人聲如沸航厚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烫罩。三九已至叠必,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背减细。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工程癌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纷妆,地道東北人亦镶。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像灼狰,于是被迫代替她去往敵國和親宛瞄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容