redux 淺談

什麼是redux


Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。html

何時使用redux


  • 某個組件的狀態,須要共享react

  • 某個狀態須要在任何地方均可以拿到redux

  • 一個組件須要改變全局狀態api

  • 一個組件須要改變另外一個組件的狀態promise

若是你的UI層很是簡單,沒有不少互動,Redux 就是沒必要要的,用了反而增長複雜性。bash

基礎和概念


stateapp

當使用普通對象來描述應用的 state 時:dom

{
  loginName: '',
  visibilityFilter: 'SHOW_COMPLETED'
}
複製代碼

這個對象就像 「Model」,區別是它並無 setter(修改器方法)。所以其它的代碼不能隨意修改它,形成難以復現的 bug。異步

actionide

  • Action 是把數據從應用傳到 store 的有效載荷。

  • 它是 store 數據的惟一來源。

  • 要想更新 state 中的數據,你須要發起一個 action

Action 就是一個普通 JavaScript 對象,像這樣:

{type: 'SET_NAME', text: 'tom'},
{type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL'}
複製代碼

強制使用 action 來描述全部變化帶來的好處是能夠清晰地知道應用中到底發生了什麼。若是一些東西改變了,就能夠知道爲何變。action 就像是描述發生了什麼的指示器。

注:

在redux中約定,action 內必須使用一個字符串類型的 type 字段來表示將要執行的動做。多數狀況下,type 會被定義成字符串常量。當應用規模愈來愈大時,建議使用單獨的模塊或文件來存放 action。

reducer

把 action 和 state 串起來,開發一些函數,這就是 reducer。

reducer 只是一個接收 state 和 action,並返回新的 state 的函數。

function loginName(state = '', action) {
  if (action.type === 'SET_NAME') {
    return action.filter;
  } else {
    return state;
  }
}

function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter;
  } else {
    return state;
  }
}

複製代碼

再開發一個 reducer 調用這兩個 reducer,進而來管理整個應用的 state:

function reducers(state = {}, action) {
  return {
    loginName: loginName(state.loginName, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  };
}

複製代碼

注:

reducer 純淨很是重要。永遠不要在 reducer 裏作這些操做:

  • 修改傳入參數;

  • 執行有反作用的操做,如 API 請求和路由跳轉;

  • 調用非純函數,如 Date.now() 或 Math.random()。

每一個 reducer 只負責管理全局 state 中它負責的一部分。每一個 reducer 的 state 參數都不一樣,分別對應它管理的那部分 state 數據。

這差很少就是 Redux 思想的所有。redux 就是提供一些簡單的工具來簡化這種模式。

redux 三大原則


  • 單一數據源

    整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。

  • State 是隻讀的

    惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。在 default 狀況下返回舊的 state。遇到未知的 action 時,必定要返回舊的 state。不要使用 Object.assign(state, newData),應該使用 Object.assign({}, state, newData)

  • 使用純函數來執行修改

    爲了描述 action 如何改變 state tree ,你須要編寫 reducers

redux API


createStore(reducer, [preloadedState], enhancer)

建立一個 Redux store來以存放應用中全部的 state。

應用中應有且僅有一個 store。

參數

  • reducer (Function): 接收兩個參數,分別是當前的 state 樹和要處理的 action,返回新的 state 樹

  • preloadedState(any): 初始時的 state。若是你使用 combineReducers建立 reducer,它必須是一個普通對象,與傳入的 keys 保持一樣的結構。不然,你能夠自由傳入任何 reducer 可理解的內容。

  • enhancer (Function): Store enhancer 是一個組合 store creator 的高階函數,返回一個新的強化過的 store creator。這與 middleware 類似,它也容許你經過複合函數改變 store 接口。

返回值

Store: 保存了應用全部 state 的對象。

Store

Store 就是用來維持應用全部的state 樹 的一個對象。

Store 不是類。它只是有幾個方法的對象。 要建立它,只須要把根部的 reducing 函數 傳遞給 createStore

Store 方法 介紹 參數 返回值
getState() 獲得state -- (any): 應用當前的 state 樹。
dispatch 分發 action。這是觸發 state 變化的唯一途徑。 action (Object) (Object): 要 dispatch 的 action。
subscribe 一個變化監聽器。每當 dispatch action 的時候就會執行,state 樹中的一部分可能已經變化。 listener (Function): 每當 dispatch action 的時候都會執行的回調。state 樹中的一部分可能已經變化。你能夠在回調函數裏調用 getState() 來拿到當前 state。 (Function): 一個能夠解綁變化監聽器的函數。
replaceReducer(nextReducer) 替換 store 當前用來計算 state 的 reducer。這是一個高級 API。只有在你須要實現代碼分隔,並且須要當即加載一些 reducer 的時候纔可能會用到它。 reducer (Function) store 會使用的下一個 reducer。 ---

combineReducers(reducers)

combineReducers 輔助函數的做用是,把一個由多個不一樣 reducer 函數做爲 value 的 object,合併成一個最終的 reducer 函數。

合併後的 reducer 能夠調用各個子 reducer,並把它們返回的結果合併成一個 state 對象。由 combineReducers() 返回的 state 對象,會將傳入的每一個 reducer 返回的 state 按其傳遞給 combineReducers() 時對應的 key 進行命名。

rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
// rootReducer 將返回以下的 state 對象
{
  potato: {
    // ... potatoes, 和一些其餘由 potatoReducer 管理的 state 對象 ... 
  },
  tomato: {
    // ... tomatoes, 和一些其餘由 tomatoReducer 管理的 state 對象,好比說 sauce 屬性 ...
  }
}
複製代碼

參數

reducers (Object): 一個對象,它的值(value)對應不一樣的 reducer 函數,這些 reducer 函數後面會被合併成一個。

返回值

(Function):一個調用 reducers 對象裏全部 reducer 的 reducer,而且構造一個與 reducers 對象結構相同的 state 對象。

注:

每一個傳入 combineReducers 的 reducer 都需知足如下規則:

  • 全部未匹配到的 action,必須把它接收到的第一個參數也就是那個 state 原封不動返回。

  • 永遠不能返回 undefined。當過早 return 時很是容易犯這個錯誤,爲了不錯誤擴散,遇到這種狀況時 combineReducers 會拋異常。

  • 若是傳入的 state 就是 undefined,必定要返回對應 reducer 的初始 state。根據上一條規則,初始 state 禁止使用 undefined。使用 ES6 的默認參數值語法來設置初始 state 很容易,但你也能夠手動檢查第一個參數是否爲 undefined。

示例

//reducers/todos.js
export default function todos(state = [], action) {
  switch (action.type) {
  case 'ADD_TODO':
    return state.concat([action.text])
  default:
    return state
  }
}
//reducers/counter.js
export default function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1
  case 'DECREMENT':
    return state - 1
  default:
    return state
  }
}
//reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'

export default combineReducers({
  todos,
  counter
})
//App.js
import { createStore } from 'redux'
import reducer from './reducers/index'

let store = createStore(reducer)
console.log(store.getState())
// {
//   counter: 0,
//   todos: []
// }

store.dispatch({
  type: 'ADD_TODO',
  text: 'Use Redux'
})
console.log(store.getState())
// {
//   counter: 0,
//   todos: [ 'Use Redux' ]
// }
複製代碼

applyMiddleware(...middlewares)

使用包含自定義功能的 middleware 來擴展 Redux 是一種推薦的方式。Middleware 可讓你包裝 store 的 dispatch 方法來達到你想要的目的。

參數

  • ...middlewares (arguments): 遵循 Redux middleware API 的函數。每一個 middleware 接受 Store 的 dispatch 和 getState 函數做爲命名參數,並返回一個函數。該函數會被傳入 被稱爲 next 的下一個 middleware 的 dispatch 方法,並返回一個接收 action 的新函數,這個函數能夠直接調用 next(action),或者在其餘須要的時刻調用,甚至根本不去調用它。調用鏈中最後一個 middleware 會接受真實的 store 的 dispatch 方法做爲 next 參數,並藉此結束調用鏈。因此,middleware 的函數簽名是 ({ getState, dispatch }) => next => action

使用

import { applyMiddleware, createStore, compose } from 'redux'
import reducers from './reducers'
import {createLogger} from 'redux-logger'
import middlePromise from 'redux-promise'
// import thunk from 'redux-thunk'

// 模擬 logger
const logger = store => next => action =>{
  console.log('prev state',store.getState())
  console.log('dispatch',action);

  let result = next(action);

  console.log('next state',store.getState());

  return result;
}

const  store = createStore(reducers, applyMiddleware(middlePromise, logger))

export default store

// 異步
import { applyMiddleware, createStore, compose } from 'redux'
import reducers from './reducers'
import {createLogger} from 'redux-logger'
// import middlePromise from 'redux-promise'
import thunk from 'redux-thunk'

// 模擬 logger
const logger = store => next => action =>{
  console.log('prev state',store.getState())
  console.log('dispatch',action);
  let result = next(action);

  console.log('next state',store.getState());

  return new Promise((resolve, reject) => {
    resolve(result)
  });
}

const  store = createStore(reducers, compose(applyMiddleware(thunk, logger)))

export default store
複製代碼

注:

  • 有的中間件有次序要求,使用前要查一下文檔

bindActionCreators(actionCreators, dispatch)

把一個 value 爲不一樣 action creator 的對象,轉成擁有同名 key 的對象。同時使用 dispatch對每一個 action creator 進行包裝,以即可以直接調用它們。

參數

  1. actionCreators (Function or Object): 一個 action creator,或者一個 value 是 action creator 的對象。

  2. dispatch (Function): 一個由 Store實例提供的 dispatch函數。

返回值

(Function or Object): 一個與原對象相似的對象,只不過這個對象的 value 都是會直接 dispatch 原 action creator 返回的結果的函數。若是傳入一個單獨的函數做爲 actionCreators,那麼返回的結果也是一個單獨的函數。

示例在react-redux中講解

compose(...functions)

從右到左來組合多個函數。

當須要把多個store 加強器 依次執行的時候,須要用到它。

參數

(arguments): **須要合成的多個函數。預計每一個函數都接收一個參數。它的返回值將做爲一個參數提供給它左邊的函數,以此類推。**例外是最右邊的參數能夠接受多個參數,由於它將爲由此產生的函數提供簽名。(compose(funcA, funcB, funcC) 形象爲 compose(funcA(funcB(funcC()))))

返回值

(Function): 從右到左把接收到的函數合成後的最終函數。

react-redux 基本使用


React-Redux 將全部組件分紅兩大類:UI 組件(presentational component)和容器組件(container component)。

UI 組件有如下幾個特徵。

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

示例

const Title = props => <h1>{props.title}</h1>;
複製代碼

容器組件

  • 負責管理數據和業務邏輯,不負責 UI 的呈現
  • 帶有內部狀態
  • 使用 Redux 的 API

React-Redux 規定,全部的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成。

connect()

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

示例

import { connect } from 'react-redux'

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(Title)
複製代碼

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

connect參數 介紹 參數
mapStateToProps mapStateToProps是一個函數。它的做用就是像它的名字那樣,創建一個從(外部的)state對象到(UI 組件的)props對象的映射關係。 第一個永遠是state;第二個參數ownProps:表明容器組件的props對象
mapDispatchToProps 用來創建 UI 組件的參數到store.dispatch方法的映射 dispatchownProps(容器組件的props對象)兩個參數

示例

const mapStateToProps = (state, ownProps) => {
 return {
   state: state,
   title: state.increment + ownProps.title
 }
}
const mapDispatchToProps = (dispatch, ownProps) => {
 return {
   actions: bindActionCreators(actionCreators, dispatch),
   ownPropsClick: () => {
     dispatch(actionCreators.increment())
     console.log(ownProps.title)
   }
 }
}

// mapDispatchToProps 也能夠是一個對象
// const mapDispatchToProps = {
//   ownPropsClick: (filter) => {
//     return {
//       type: 'INCREMENT',
//       filter: filter
//     }
//   }
// }

export default connect(mapStateToProps, mapDispatchToProps)(Test)
複製代碼

使組件層級中的 connect() 方法都可以得到 Redux store。正常狀況下,你的根組件應該嵌套在 中才能使用 connect() 方法。

屬性

  • store (Redux Store): 應用程序中惟一的 Redux store 對象
  • children (ReactElement) 組件層級的根組件。

示例

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  rootEl
)
複製代碼

redux的使用先講到這裏,若有錯誤之處但願你們積極留言!

相關文章
相關標籤/搜索