Redux 學習總結

1.Redux 設計理念

  Web 應用是一個狀態機,視圖與狀態是一一對應的javascript

  全部的狀態,保存在一個對象裏面java

 

2.基本概念和API

  Redux 的核心就是 store, action, reducer   store.dispatch(action) ——> reducer(state, action) ——> final statereact

(1)store 就是保存數據的地方,redux 提供createStore 函數,生成Storeredux

    store = redux.createStore(reducer, []);數組

        store.getState() //返回store的當前狀態promise

  Store 容許使用store.subscribe方法設置監聽函數,一旦 State 發生變化,就自動執行這個函數。babel

  store.subscribe(listener);app

  store.subscribe 方法返回一個函數,調用這個函數就能夠解除監聽異步

  let unsubscribe = store.subscribe(() =>函數

    console.log(store.getState())

  );

  unsubscribe(); //解除監聽

  Store 的實現

store.getState() //獲取當前狀態

store.dispatch() //觸發action

store.subscribe() //監聽state狀態

import { createStore } from ‘redux’;

let { subscribe, dispatch, getState } = createStore(reducer, window.STATE_FORM_SERVER);

window.STATE_FORM_SERVER //是整個應用的初始狀態值

 (2)action 是一個普通的object,必須有一個type屬性,代表行爲的類型。

  const action = {

    type: ’add_todo’,

    text: ‘read’,

    time

    …

  }

  action描述當前發生的事情,改變State的惟一方法就是經過action,會將數據送到store。

  通常用actionCreator 工廠模式產生,View要發出的消息類型對應action的類型,手寫起來很費勁。

  const ADD_TODO = 「添加 todo」;

  function addTodo(text){

    return {

           type: ADD_TODO,

      text 

          }

  }

  const action = addTodo(‘Learn’);

  addTodo 方法就是一個Action Creator

  View 發出Action的惟一途徑 store.dispatch(action) //觸發事件 

 (3)reducer 其實就是一個普通函數,主要用來改變state. Store 收到View 發出的Action 之後,必須返回一個新的State,View 纔會發生變化。 而這個計算新的State的過程就叫Reducer.

  const reducer = function(state, action){

  switch(state.text){

             case ‘add_todo’:

  return state.contact(‘…’);

              default:

  return state;

         }

  }

   固然實際開發不像上面例子這麼簡單,須要在建立state的時候就知道state的計算規則,將reducer傳入:

  store = redux.createStore(reducer);

  Reducer 純函數,只要有一樣的輸入必然返回一樣的輸出。不能改變原來的state而是經過Reducer返回一個新的state。

//state 是一個對象

function reducer(state, action){

return Object.assign({},state, {thingToChange});

         return {…state, …newState};

}

//state 是一個數組

function reducer(state, action){

return […state, newItem];

}

3.Reducer的拆分和合並

在實際項目中,reducer 很龐大,不易閱讀管理,咱們能夠將reducer 拆分紅小的函數,不一樣的函數對應處理不一樣的屬性。而後將其合併成一個大的reducer

Reducer 提供了一個方法combineReducers方法來合併reducer.

const chatReducer = (state = defaultState, action = {}) => {
  return {
    chatLog: chatLog(state.chatLog, action),
    statusMessage: statusMessage(state.statusMessage, action),
    userName: userName(state.userName, action)
  }
};

import { combineReducers } from 'redux';

const chatReducer = combineReducers({
  chatLog,
  statusMessage,
  userName
})

export default todoApp;

你能夠把全部子reducers 放在一個文件夾裏,而後統一引入。

import { combineReducers } from 'redux'

import * as reducers from './reducers'

const reducer = combineReducers(reducers)

4.中間件和異步操做

咱們使用redux ,用戶發出action,Reducer算出新的state,而後從新渲染界面。這裏Reducer是當即算出state,當即響應的,同步執行的順序。 

但是若是咱們須要執行異步實現,Reducer執行完以後,自動執行呢? 這裏就須要用到middleWare(中間件)。

中間件加在什麼地方合適?咱們來簡單分析下。

1. Reducer 是純函數,用來計算state,相同的輸入必然獲得相同的輸出,理論上純函數不容許讀寫操做。

2. View和state是一一對應,是state的呈現,沒有處理能力。

3. Action 是存放數據的對象,即消息的載體,被觸發操做。

最後發現,只有加在store.dispatch() 比較合適。添加日誌功能,把 Action 和 State 打印出來,能夠對store.dispatch進行以下改造。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

總結:中間件其實就是一個函數,對store.dispatch方法進行了改造,在發出 Action 和執行 Reducer 這兩步之間,添加了其餘功能。

中間件的用法:

const store = createStore( reducer, initial_state, applyMiddleware(logger) );

createStore方法能夠接受整個應用的初始狀態做爲參數,將中間件(logger)放在applyMiddleware方法之中,傳入createStore方法,就完成了store.dispatch()的功能加強。

applyMiddleware 是Redux 的原生方法,做用是將全部中間件組成一個數組,依次執行。下面是它的源碼:

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {...store, dispatch}
  }
}

能夠看到,中間件內部(middlewareAPI)能夠拿到getState和dispatch這兩個方法。

異步操做的一個解決方案,就是讓 Action Creator 返回一個 Promise 對象。看一下redux-promise的源碼:

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }

    return isPromise(action.payload)
      ? action.payload.then(
          result => dispatch({ ...action, payload: result }),
          error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          }
        )
      : next(action);
  };
}

從上面代碼能夠看出,若是 Action 自己是一個 Promise,它 resolve 之後的值應該是一個 Action 對象,會被dispatch方法送出(action.then(dispatch)),但 reject 之後不會有任何動做;若是 Action 對象的payload屬性是一個 Promise 對象,那麼不管 resolve 和 reject,dispatch方法都會發出 Action。

5.React+Redux

redux將全部組件分爲UI組件和容器組件。

UI 組件有如下幾個特徵。

  1. 只負責 UI 的呈現,不帶有任何業務邏輯
  2. 沒有狀態(即不使用this.state這個變量)
  3. 全部數據都由參數(this.props)提供
  4. 不使用任何 Redux 的 API

UI 組件又稱爲"純組件",即它純函數同樣,純粹由參數決定它的值。

 

容器組件的特徵偏偏相反。

  1.負責管理數據和業務邏輯,不負責 UI 的呈現

  2.帶有內部狀態

  3.使用 Redux 的 API

React-Redux 提供connect方法,用於從 UI 組件生成容器組件。connect的意思,就是將這兩種組件連起來。

import { connect } from 'react-redux'

const VisibleTodoList = connect(

  mapStateToProps,

  mapDispatchToProps

)(TodoList)

上面代碼中,connect方法接受兩個參數:mapStateToProps和mapDispatchToProps。

它們定義了 UI 組件的業務邏輯。前者負責輸入邏輯,即將state映射到 UI 組件的參數(props),後者負責輸出邏輯,即將用戶對 UI 組件的操做映射成 Action。

 

6. react+redux 的 simple 示例

原理圖:

<div id="container"></div>

<script type="text/babel">

  //工廠Action

    var addTodoAction = function(text){

      return { type: 'add_todo', text: text } }; var todoReducer = function(state,action){ if(typeof state === 'undefined') return []; switch(action.type){ case 'add_todo': return state.slice(0).concat({ text: action.text, completed:false }); default: return state; } }; var store = redux.createStore(todoReducer); var App = React.createClass({ //獲取初始狀態  getInitialState: function(){ return { items: store.getState() }; }, componentDidMount: function(){ var unsubscribe = store.subscribe(this.onChange); }, onChange: function(){ this.setState({ items: store.getState() }); }, handleAdd: function(){ var input = ReactDOM.findDOMNode(this.refs.todo); var value = input.value.trim(); if(value){ store.dispatch(addTodoAction(value)); } input.value = ''; }, render: function(){ return( <div> <input ref="todo" type="text" placeholder="input todo type"/> <button onClick ={this.handleAdd}>Add</button> <ul> { this.state.items.map(function(item){ return <li>{item.text}</li>  }) } </ul> </div>  ); } }); ReactDOM.render( <App />,  document.getElementById('container') ) </script>
相關文章
相關標籤/搜索