node.js學習之react,redux,react-redux

redux好難懂,終於明白了一點,做了個小demo,記錄一下。

先看目錄結構:

src

|--index.js

|--actions

  |--index.js

|--components

  |--Additem.js

  |--App.js

  |--ItemList.js

|--reducers

  |--index.js

最終效果如圖:

wKioL1bmYvmCVI9BAAAS3dfD9V8501.png

wKiom1bmYnLRPorxAAAc9lYsA64275.png

wKiom1bmYnOiEYK7AAAZBCs4Q4Y173.png

redux三大件:actions , reducers, store ,

Action 是把數據從應用(譯者注:這裏之所以不叫 view 是因爲這些數據有可能是服務器響應,用戶輸入或其它非 view 的數據 )傳到 store 的有效載荷。它是 store 數據的唯一來源。一般來說你會通過store.dispatch() 將 action 傳到 store。


這是官方的解釋,我個人的理解是 action就是定義一個事件,但不做任何邏輯上的操作,只是把你需要處理的數據和事件綁定到一起,然後等待store進行派發,也就是更新store。就好像定義了一個按鈕的點擊事件 ;

btn.addEventListener(MouseEvent.CLICK,onClickHandle);

這句代碼實際上什麼操作也沒有做,只是告訴系統有MouseEvent.CLICK消息的時候應該做什麼。


reducer Action 只是描述了有事情發生了這一事實,並沒有指明應用如何更新 state。而這正是 reducer 要做的事情。


這是官方解釋,我的理解是就像上面說的onClickHandle函數,就是在等待事件被觸發的時候要執行的代碼,當有action被派發出來的時候,store就會根據定義來找到這個函數去執行,然後改變store,從而引起react的重新渲染,最終使view發生改變。


Store 就是把action 和 reducer 聯繫到一起的對象。Store 有以下職責:

 Redux 應用只有一個單一的 store。當需要拆分處理數據的邏輯時,使用 reducer 組合 而不是創建多個 store。

我的理解是store就是一個事件監聽器加事件派發器加狀態記錄器。他會根據被觸發的action來找到對應的reducer。


我在寫這個demo的時候是先定義了action,也就是會有哪些操作,然後根據這些action去寫reducer,也就是每個事件應該怎麼處理。最後寫了界面,當然,也可以先寫界面,根據界面定義action。

下面看看代碼是怎麼寫的,很簡單並且很簡陋的一段代碼,

src/actions/index.js:

"use strict";

export const ADD_ITEM = 'add_item';
export const DEL_ITEM = 'delete_item';

export function addItem(text){
  return {
    type:ADD_ITEM,
    text
  }
}

export function delItem(index){
  return {
    type:DEL_ITEM,
    index
  }
}


這裏定義了兩個事件,一個是添加記錄,一個是刪除記錄,每個方法都是返回一個對象,對象中必須有type屬性,這個type屬性實際上纔是真正的事件區分器,

然後additem中的text是要添加記錄的內容,index是要刪除記錄的所引。到這,action就完了。

reducers/index.js:

"use strict";

import { combineReducers } from 'redux';
import { ADD_ITEM, DEL_ITEM } from '../actions/index';

const initalState = {
  items:[]
};

function aitems(state=[],action){
  switch (action.type){
    case ADD_ITEM:
      return state.concat([action.text]);
    case DEL_ITEM:
      state.splice(action.index,1);
      return [...state];
    default:
      return state;
  }
}

let todos=combineReducers({
  items:aitems
});
export default todos;



這個文件裏的內容就是根據action做的操作,通過type來區分發生了什麼事,當應該添加一條記錄的時候,就把text內容保存到數組中並返回,

這裏有幾點需要說明:

1,initalState{} 初始state,裏面有一個屬性items,數組類型,當然可以叫別的名字。

2,aitems的第一個參數state=[],這裏爲什麼會讓state賦值一個數組類型呢?不應該是object麼?這是 因爲在給todos賦值那一句,items:aitems,我對這點的理解是當觸發事件的時候,redux會自動遍歷todos,然後把state和combineReducers中傳入的對象進行比較,遇到同名屬性,就把state中的同名屬性值傳給combineReducers中的同名屬性所對應的函數,有點繞!!!!

3,aitems的第二個參數action,你想的沒錯,就是剛纔我們定義的 src/actions/index.js中的方法所   返回的對象。

4,combineReducers函數是一個很有個性的函數,不多說了。


src/index.js:

"use strict";
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import  App from './components/App';
import todos from './reducers/index';

let store = createStore(todos);

let element = document.getElementById('app');
render(
  <Provider store={store}>
    <App />
    </Provider>,
  element
);



這個要說的不太多,因爲每個講react和redux有關的文章裏大部分會有這一段,主要是爲了把react 和redux 通過 react-redux連接起來。

下面看組件:

src/components/App.js:

"use strict";

import React from 'react';
import {connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import  * as actions from '../actions/index';
import AddItem from './AddItem';
import ItemList from './ItemList';

class App extends React.Component{
  render(){
    console.log("app");
    return <div>
      <AddItem onAddClick={(text) => this.props.actions.addItem(text)} />
      <ItemList items={this.props.items} onDelClick={index=>this.props.actions.delItem(index)}/>
      </div>
  }
}
export default connect(({items})=>({items}),(dispatch) => ({actions:bindActionCreators(actions,dispatch)}))(App);


代碼很簡單,一看就能明白,唯一複雜一點的就是最後一句:


export default connect(({items})=>({items}),(dispatch) => ({actions:bindActionCreators(actions,dispatch)}))(App);


connect接收兩個對象,第一個對象是store中的state,也可以是一部分,會變成綁定view的props屬性,第二個對象是actions,也會變成當前view的props屬性,這樣就可以直接調用同名的actions,會自動派發對應的事件。connect函數執行過後會返回一個函數,再把當前的view傳進去,就可以把當前的view,state,action綁定到一起,這樣就能在當前的view中使用state的數據和派發事件了。


然後還有兩個組件:

src/components/Additem.js:

"use strict";
import React from 'react';

export default class AddItem extends React.Component{
  static propTypes = {
    onAddClick:React.PropTypes.func
  };
  _clickHandle(e){
    console.log("additemclick");
    this.props.onAddClick(this.refs.itemText.value.trim());
  }
  render(){
    console.log("additem");
    return <div>
        <input type="text" ref="itemText"/><br/>
        <button onClick={this._clickHandle.bind(this)}>add</button>
      </div>
  }
}


這就是普通的react組件了,從父級接收一些參數來使用,或者接收一些函數來實現在子組件中調用父組件的函數。

src/components/ItemList.js:

"use strict";

import React from 'react';

export default class ItemList extends React.Component{
  static propTypes={
    items:React.PropTypes.array.isRequired,
    onDelClick:React.PropTypes.func
  };
  _onDelClick(index){
    console.log("itemlistclick",index);
    this.props.onDelClick(index);
  }
  render(){
    console.log("itemlist");
    console.log(this.props.items);
    return <ul>
      {
        this.props.items.map((value,index) =>
          <li>{index},{value}<button onClick={this._onDelClick.bind(this,index)}>del</button></li>
          )}
    </ul>
  }
}


這個,同上,不再多做解釋。

恩。demo到這裏,主要代碼就完了,再來梳理一遍:

1,src/index.js把所有的包關聯起來。

2,定義actions,定義reducers。

3,把state和actions綁定到App.js上。

4,從App.js中把state和actions分別傳進子組件。

5,子組件中有操作,就會調用actions.

6,actions被觸發,store調用對應的reducers.

7,reducers被調用,修改state.

8,react檢測到state被修改,重新渲染組件,改變views。

恩。又跨近了一步。

參考:http://camsong.github.io/redux-in-chinese/docs/basics/ExampleTodoList.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章