Redux basic tutorial

@(blog)react

Redux basic tutorial

本文的讀者爲了解flux概念,熟悉react,瞭解es6語法的同窗git

redux 是最近很火的一個 flux 框架,短短的一個月如今已經有2900+的 star 了,watch以後天天收到幾百封 pr 的郵件,廢話就很少說了。
爲何要用 redux ,請看鏈接 The Evolution of Flux Frameworks程序員

主要特色es6

  • Everything (Stores, Action Creators, configuration) is hot reloadable. —— 配合起 hotloader 應該特別爽,雖然如今用的是 browserify,好處感受並不明顯。
  • store 中的數據不受限制,能夠是 number object array 等等,廢話,由於它的 store 只是簡單的函數。
  • 提供 devtools ,監控 action 的觸發以及 state 的變化。
  • 源碼清晰簡單,輕量級,根本不須要文檔,直接看源碼就好了....缺點就是若是看不懂源碼,光看文檔會以爲不夠清晰。
  • api 很精簡,不用記茫茫多的 api
  • every thing is simple function
  • connecterprovider 這兩個東西用起來總以爲很繁瑣,不那麼優雅。

下面經過寫的一個簡單counter的例子 介紹 redux 的核心方法以及一些須要注意的地方。github

  • 同步與異步兩種actionCreator
  • middleware的使用
  • dispatch actions
  • 拿到並同步state

代碼放在https://github.com/yeatszhang/redux-tutorial, 須要安裝gulp編程

代碼是基於分支 v1.0.0-rc api略微有些區別,詳情見 Breaking API changes for 1.0redux

actionCreator

actions creator 是用來生成 action 的函數,在默認狀況下能夠接受返回object或者function 的函數,不少人學習flux的時候把action與actionCreator弄混淆....:gulp

// 直接返回object
actionCreators.addTodo = function(text) {
  return {
    type: types.ADD_TODO,
    text
  };
}

// 返回函數
actionCreators.incrementAsync = function() {
  return (dispatch, getState) => {
    // actionCreator中能夠經過getState得到當前的state
    console.log(getState());
    // 異步action
    setTimeout(() => {
      dispatch({
        type: INCREMENT_COUNTER2,
      });
    }, 1000);
  };
};

在沒有使用任何 middleware 的狀況下,只有這有兩種 action 能夠被 dispatchapi

app

在動態內容的最外層應該使用Provider進行包裹,provider接收store做爲參數,注意children是一個函數並非reactElement
provider將store做爲context往子節點進行傳遞,並實現store的熱替換。所以在provider內的組件其實能夠不經過connect來拿到dispatch以及state,而直接經過context拿到store對象,不過做者不推薦這麼作。app

import React from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
// redux midlleware repositories
import thunk from 'redux-thunk';
// 將 redux 與 react 相關的部分,如 connector provider 單獨抽取出來
import { Provider } from 'react-redux';
import reducers from '../reducers';
import CounterApp from './CounterApp.js';
import logMiddleware from '../middleWares/logMiddleware.js';

const reducer = combineReducers(reducers);
const createStoreWithMiddleware = applyMiddleware(thunk, logMiddleware)(createStore);
const store = createStoreWithMiddleware(reducer);
// 使用middleWare thunk, 若是沒有自定義中間層的需求能夠直接寫
// const store = createStore(reducer);

class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        {() => <CounterApp />}
      </Provider>
    );
  }
}

smart component

smart component擁有兩個特色:

  1. 自動關聯store中的state並自動re-render
  2. 能夠經過dispatch來分發事件,從而觸發store的更新

剛接觸redux的同窗確定會以爲這個connect很難以理解。仍是在代碼裏面說把。。。

/**
 * Created by yichizhang on 15/7/26.
 */

import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { Connector } from 'react-redux';
import Counter from '../components/Counter';
import actionCreators1 from '../actionCreators/actionCreators1.js';
import actionCreators2 from '../actionCreators/actionCreators2.js';

// state 是各reducer中state的集合
function select(state) {
  // 從各reducer中挑選出component須要監聽的state
  return {
    counter1: state.reducer1.counter,
    counter2: state.reducer2.counter,
  };
}

export default class CounterApp extends Component {
  // select函數的返回值會與dispatch組裝程一個object做爲參數
  // 從這裏看出connector就是幫忙拿到provider中store的dispatch方法以及挑選出須要使用的state
  renderChild({ counter1, counter2, dispatch}) {
    // 我的以爲這樣使用action十分不方便,尤爲是當組件只須要觸發actions不須要監聽store的變化的時候。我會偷懶經過context去拿到dispatch~~
    const actions1 = bindActionCreators(actionCreators1, dispatch);
    const actions2 = bindActionCreators(actionCreators2, dispatch);
    const props = { ...actions1, ...actions2, counter1, counter2 };
    // 全部的action以及state都會以props的形式提供給Counter,而後在Counter裏面就能夠隨心所欲了~
    return <Counter {...props} />;
  }

  render() {
    return (
      <Connector select={select}>
        {this.renderChild}
      </Connector>
    );
  }
}

reducer

redux認爲程序員不須要去寫store中的邏輯而只須要寫明對state的處理邏輯就好:

old  sate => action  => new state

這是一個徹底同步的過程。reducer只須要聲明初始狀態以及state在接收到action以後的改變規則就能夠了。

import React from 'react/addons';
import {INCREMENT_COUNTER1, DECREMENT_COUNTER1} from '../constants/actionsTypes.js';
const update = React.addons.update;

// state能夠是任何類型
const initialState = {
  counter: 0,
};

// reducer只是一個簡單的switch方法
export default function counter(state = initialState, action = null) {
  switch (action.type) {
    case INCREMENT_COUNTER1:
      // 須要注意的是connector當select中的state發生變化時會作一個shallow equal的操做,
      // 因此若是須要操做引用值的時候必定不能直接賦值,須要使用addon中的update或者immutable.js,知道看到這兩個工具又不想繼續學了..其實很簡單
      // 這樣能夠大大避免重複的render,從而提升性能
      return update(state, {
        counter: {
          $set: state.counter + 1,
        },
      });
    case DECREMENT_COUNTER1:
      return update(state, {
        counter: {
          $set: state.counter - 1,
        },
      });
    default:
      return state;
  }
}

middleWare

感興趣的同窗能夠看看,通常來講默認的thunk就夠用了。我在例子里加了個log的中間層

// 打印觸發的action
function logMiddleware() {
  // 這裏的next是下一個middleWare
  return function(next) {
    return function(action) {
      // 打印此action並使用下一個middleWare處理該action
      console.log(action);
      next(action);
    };
  };
}

export default logMiddleware;

// 下面是默認的thunk middleWare

function thunkMiddleware(_ref) {
  var dispatch = _ref.dispatch;
  var getState = _ref.getState;

  return function (next) {
    return function (action) {
      // 若是是函數則將dispatch與getState做爲參數執行函數,不然交給寫一個middleware處理
      return typeof action === 'function' ? action(dispatch, getState) : next(action);
    };
  };
}

結語

其實redux不明白的地方直接看源碼更好,redux的代碼量很小並且組織也很清晰,建議你們都去看,不過做者貌似函數式編程的思惟很重,大量使用修飾器的語法,還有reduce~ 挺繞的~

以後會總結本身閱讀redux源碼的一些心得,以及各功能模塊的實現原理~

相關文章
相關標籤/搜索