1.什麼是Redux
管理Web應用全域性狀態的框架。
單頁面應用,顧名思義,和傳統專案的最明顯區別就是專案只有一個頁面,頁面有一個根元素,我們寫的每一個頁面是一個大的元件,前端接管路由來渲染不同的頁面元件。
隨著應用的複雜,前端需要儲存更多的state,包括快取的全域性資料,本地生成尚未持久化到伺服器的資料,也包括 UI 狀態,如啟用的路由,被選中的標籤,是否顯示載入動效或者分頁器等等。
如果是單純的資料快取 也沒什麼需要考慮的東西,放到記憶體就可以了, 重點是還要讓state和view有繫結的關係。state的變化能夠觸發view的變化。
在我們的專案中,沒有用redux之前,我們都是頁面元件管理state,出現以下需求的時候,寫起來就比較複雜
-
某個元件的狀態,需要共享
-
某個狀態需要在任何地方都可以拿到
-
一個元件需要改變全域性狀態
-
一個元件需要改變另一個元件的狀態
2.Redux的原則和設計思想
2.1 三大原則
-
單一資料來源:整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於唯一一個 store 中
-
State 是隻讀的:惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通物件。
-
使用純函式來執行修改: 為了描述 action 如何改變 state tree ,你需要編寫 reducers。只要是同樣的輸入,必定得到同樣的輸出。
純函式是函數語言程式設計的概念,必須遵守以下一些約束。
不得改寫引數
不能呼叫系統 I/O 的API
不能呼叫Date.now()或者Math.random()等不純的方法,因為每次會得到不一樣的結果
2.2 設計思想:
(1)Web 應用是一個狀態機,檢視與狀態是一一對應的。
(2)所有的狀態,儲存在一個物件裡面。
3.Redux中的概念
3.1 Action
執行的動作,包括動作所需要的資料,改變store資料的唯一來源,一般是通過store.dispatch() 將 action 傳到 store。
Action 本質上是 JavaScript 普通物件。
flux-standard-action FSA標準
-
type,必須,string或者Symbol
-
payload,可選,執行action所需要的資料,任何型別
-
error,可選,標識這個action是否有錯誤,true or false
-
meta,可選,任何型別,payload之外的其他資料。
3.2 Reducer
根據action 做更新state的操作。
Action 只是描述了有事情發生了這一事實,並沒有指明應用如何更新 state。而這正是 reducer 要做的事情。
3.3 Store
Store就是儲存全域性state的容器,儲存三個常用的api
-
getState,獲取當前的state
-
subscribe,給store變化新增監聽函式
-
dispatch,用於接受action的方法,觸發reducer和監聽函式
3.4 單項資料流
使用者通過View,dispatch 相應的action,store呼叫reducer獲取最新的state,並觸發通過subscribe訂閱的監聽函式,監聽函式中我們通過store的getState方法獲取最新的state,更新view。
工作流程圖
)
4.應用例項
simpleredux/index.js
import {createStore} from `redux`;
export function createAction(type, payload) {
return {
type,
payload
}
}
const initialState = {
time: new Date().getTime()
}
function reducer(state = initialState, action) {
switch (action.type) {
case `NOW_TIME`:
return {
...state,
time: action.payload
}
default:
return state;
}
}
let store;
export function getStore() {
if(store) return store;
return store = createStore(reducer);
}
TestRedux.js
`use strict`;
import React, { Component } from `react`;
import {
StyleSheet,
View,
Text
} from `react-native`;
import MtButton from `@scfe/react-native-button`;
import {getStore, createAction} from `../../simpleredux/index`;
const store = getStore();
class TestRedux extends Component {
constructor(props) {
super(props);
let state = store.getState();
this.state = {
time: state.time
};
store.subscribe(()=>{
let state = store.getState();
this.setState({
time: state.time
});
});
}
_sendAction() {
let action = createAction(`NOW_TIME`, new Date().getTime());
store.dispatch(action);
}
render() {
return (
<View style={styles.container}>
<Text>{this.state.time}
</Text>
<MtButton text="發出action" onPress={this._sendAction.bind(this)} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 40
}
});
export default TestRedux;