1.Redux 是一個專門用來管理數據業務或邏輯狀態的框架,它也可以實現代碼結構的規范化並提供組件之間通信的便利,而這兩點,對於大型應用來說非常關鍵。
2.工作流程:
Redux 三大原則
-
單一數據源 整個應用的 state 被存儲在一個 Object tree 中,且只存在於唯一的Store中。
-
state 是只讀的 唯一改變 state 的方法就是觸發 action,action 是一個用於描述發生事件的普通對象,視圖部分只需要表達想要修改的意圖,所有修改都會被集中化處理。
-
使用純函數來執行修改 為了實現根據 action 修改 state值,我們就需要編寫 Reducer。它是一個純函數,接收先前的 state 和 action 返回新的 state ,隨着應用的變大,你可以將它拆成多個小的 Reducer ,分別獨立操作 state tree 中的不同部分。
2.具體工作步驟
Action
const add =()=>{ return { type:"add", data:id, } }
上邊函數返回的就是一個 Action,它是一個包含 type 和 data 的對象。 Action 的作用就是告訴狀態管理器需要做什么樣的操作,正如上邊的例子,就是要添加一條信息,這樣就定義了一個Action,而 data 就是你做這個操作需要的數據。
Reducer
reducer 是一個函數(純函數),接受 舊 state 和 action,根據不同的 Action 做出不同的操作並返回新的 state 。即:(state, action) => state
const reducer = (state,action)=>{ switch(action.type){ case "add": state['newItemId'] = action.data; return {...state}; case "delete": delete state.newItemId; return {...state}; default : return state; } }
在沒有任何操作的情況下,我們返回的 state 與原 state 相同。
Store
import { createStore } from 'redux'; const store = createStore(reducer);
這就是 store, 用來管理 state 的單一對象,其中有三個方法:
-
store.getState()
:獲取state ,如上,經過 reducer 返回了一個新的 state,可以用該函數獲取。 -
store.dispatch(action)
:發出 action,用於觸發 reducer 更新 state, -
store.subscribe(listener)
:監聽變化,當 state 發生變化時,就可以在這個函數的回調中監聽。
React-Redux
Redux 官方提供的 React 綁定庫。
容器組件與傻瓜組件
在應用中,通常容器組件對於 Redux 可知,他們的子組件應該是"傻瓜的"(傻瓜組件),並且通過porps獲取數據。 容器組件: 通過組件 state 屬性維護自身及其子組件的數據,它可以向 Redux 發起 action ,從 Redux 獲取 新state值。 傻瓜組件: 通過 props 調用回調函數,從 props 獲取數據展示。
注入 Store
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; import { Provide } from 'react-redux'; import reducer from './reducer'; import App from './app'; const store = createStore(reducer); class RootComp extends React.Component{ render(){ //... return (//將整個視圖結構包進 Provider <Provider store={store}> <App/> </Provider> ) } } ReactDOM.render(<RootComp/> ,document.getElementById('root'));
connect#
該方法用於從 UI 組件生成容器組件,
import React from 'react'; import { connect } from 'react-redux'; import Home from './home'; class AppContainer extends React.Component{ render( return ( <Home/> ); ) } const App = connect()(AppContainer);//這里的App就是生成的容器組件。 export default App;
connect( mapStateToProps , mapDispatchToProps , mergeProps , options )();
連接 React 組件與 Redux Store
-
mapStateToProps
該參數為一個function mapStateToProps (state,[ownProps]){...}
,定義該參數后組件就可以監聽 Redux Store 的變化,任何時候只要store發生變化,該函數就會被調用,該函數必須返回一個純對象,它會與組件的 props 結合,而第二個參數 ownProps 被指定時,它代表傳遞到該組件的props,且只要組件收到新的 props ,mapStateToProps 就被調用。 -
mapDispatchToProps
通常我們會將該參數省略,此時默認情況下,dispatch 會注入到組件的props中,在你需要出發 action 的地方(可能是某個事件函數)使用該disaptch 函數將 action 發出。
例子:
在上邊例子中,我們只要點擊下添加鏈接,組件就會通過點擊事件觸發 dispath 函數 發送 action 到 store 中的 reducer, reducer 則根據 action 的 type 來決定執行什么操作,之后在 store 中新增一條記錄(newItemId)后返回一個新的 state (里邊包含Store中發生改變后的所有值),由於組件使用 connect 將自己與 Store 綁定起來,Store 中的值發生變化就會執行 mapStateToProps,將新的 state 放入組件的 props,從而引發組件的渲染。
3. 異步中間件
也許你也已經發現了前邊的介紹中有個關鍵的問題沒解決,那就是異步操作(最典型的就是通過ajax訪問后台數據)。Action 發出以后,Reducer 立即計算出 State,這叫同步,Action 發出之后,過段時間再執行 Reducer,這就是異步。 如和何才能讓 Reducer 在異步操作結束后自動執行呢?這就需要異步中間件來解決。
中間件通過對 store.dispatch 進行改造,在發出 Action 和執行 Reducer 這兩步之間添加其他功能。 中間件可以做很多事情,這里主要講解異步中間件redux-promise-middleware
的使用。
//Store.js 加載中間件 import { applyMiddleware,createStore } from 'redux'; import createPromiseMiddleware from 'redux-promise-middleware'; import reducer from './reducers/index'; const store = createStore({//createStore 可以接受應用初始狀態作為參數,這時, `applyMiddleware` 就是第三個參數。 reduer, applyMiddleware([createPromiseMiddleware()]) }); export default store;
發起異步action#
//actions.js export const getTodos = () => { const apiUrl = `/todos`; return { type: GET_TODO, //屬性 payload 值為 Promise 類型(中間件據此判斷是否需要異步處理) payload: fetch(apiUrl).then(response => { if (!response.ok) { //請求返回的可能是500等錯誤,也會執行到這里,需將狀態設置為 rejected return Promise.reject(response.status); } return response.json(); }) }; }
處理異步 actions#
一般 action 的處理,發起和處理是一一對應的,而異步 action 則有三個 action 來處理,分別對應異步的三種狀態
-
${GET_TODO}_PENDING
異步請求執行后會立刻 dispatch 此 action,表示請求已發出,正在等待返回值 -
${GET_TODO}_FULFILLED
異步請求成功后會 dispatch 此 action,注意 Response 只要回來,即使是 500,也會執行到這里 -
${GET_TODO}_REJECTED
異步請求失敗后會 dispatch 此 action
上述三個 action 及后綴皆為中間件
redux-promise-middleware
的約定,不同中間件約定可能不一致。
//reducer.js case `${GET_TODO}_PENDING`: { return { status: FetchStatus.LOADING, } } case `${GET_TODO}_FULFILLED`: { return {status: FetchStatus.SUCCESS, items: action.payload}; } case `${GET_TODO}_REJECTED`: { return {status: FetchStatus.FAILURE, error: action.payload}; }