學過react的都知道,react用state
和props
控制組件的渲染狀況,而對於JavaScript單頁面日趨複雜的今天,JavaScript須要管理愈來愈多的state,而這些state包括着各類亂七八糟途徑來的數據。甚至有的應用的state會關係到另外一個組件的狀態。因此爲了方便對這些state的管理以及對state變化的可控性。這個時候Redux這個東西就出來了,它可讓state的變化變得可預測。前端
什麼是redux?這裏非權威的解釋:就是一個應用的state管理庫,甚至能夠說是前端數據庫。更包括的是管理數據。react
state是整個應用的數據,本質上是一個普通對象。
state決定了整個應用的組件如何渲染,渲染的結果是什麼。能夠說,State是應用的靈魂,組件是應用的肉體。
因此,在項目開發初期,設計一份健壯靈活的State尤爲重要,對後續的開發有很大的幫助。
可是,並非全部的數據都須要保存到state中,有些屬於組件的數據是徹底能夠留給組件自身去維護的。git
數據state已經有了,那麼咱們是如何實現管理這些state中的數據的呢?那就是action,什麼是action?按字面意思解釋就是動做,也能夠理解成,一個可能!改變state的動做包裝。就這麼簡單。。。。
只有當某一個動做發生的時候纔可以觸發這個state去改變,那麼,觸發state變化的緣由那麼多,好比這裏的咱們的點擊事件,還有網絡請求,頁面進入,鼠標移入。。。因此action的出現,就是爲了把這些操做所產生或者改變的數據從應用傳到store中的有效載荷。 須要說明的是,action是state的惟一來源。它本質上就是一個JavaScript對象,可是約定的包含type
屬性,能夠理解成每一個人都要有名字通常。除了type屬性,別的屬性,均可以.
那麼這麼多action一個個手動建立必然不現實,通常咱們會寫好actionCreator
,即action的建立函數。調用actionCreator
,給你返回一個action。這裏咱們可使用 redux-actions,嗯呢,咱們下文有介紹。
好比有一個counter數量加減應用,咱們就有兩個action,一個decrement
,一個increment
。 因此這裏的action creator寫成以下:github
export function decrement() { return{ type:DECREMENT_COUNTER } } export function increment(){ return{ type:INCREMENT_COUNTER } }
那麼,當action建立完成了以後呢,咱們怎麼觸發這些action呢,這時咱們是要利用dispatch
,好比咱們執行count增減減小動做。數據庫
export function incrementIfOdd(){ return(dispatch,getState)=>{ const {counter} = getState(); if(counter%2==0) { return; } dispatch(increment()); } } export function incrementAsync() { return dispatch => { setTimeout(() => { dispatch(increment()); }, 1000); }; }
爲了減小樣板代碼,咱們使用單獨的模塊或文件來定義 action type 常量npm
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
這麼作不是必須的,在大型應用中把它們顯式地定義成常量仍是利大於弊的。json
既然這個可能改變state的動做已經包裝好了,那麼咱們怎麼去判斷而且對state作相應的改變呢?對,這就是reducer乾的事情了。reducer
是state最終格式的肯定。它是一個純函數,也就是說,只要傳入參數相同,返回計算獲得的下一個 state 就必定相同。沒有特殊狀況、沒有反作用,沒有 API 請求、沒有變量修改,單純執行計算。reducer
對傳入的action進行判斷,而後返回一個經過判斷後的state,這就是reducer的所有職責。如咱們的counter應用:redux
import {INCREMENT_COUNTER,DECREMENT_COUNTER} from '../actions'; export default function counter(state = 0, action) { switch (action.type){ case INCREMENT_COUNTER: return state+1; case DECREMENT_COUNTER: return state-1; default: return state; } }
這裏咱們就是對增和減兩個以前在action定義好的常量作了處理。
對於一個比較大一點的應用來講,咱們是須要將reducer拆分的,最後經過redux提供的combineReducers方法組合到一塊兒。 如此項目上的:數組
const rootReducer = combineReducers({ counter }); export default rootReducer;
每一個reducer
只負責管理全局state
中它負責的一部分。每一個reducer
的state
參數都不一樣,分別對應它管理的那部分state
數據。combineReducers()
所作的只是生成一個函數,這個函數來調用你的一系列reducer
,每一個reducer
根據它們的key
來篩選出state
中的一部分數據並處理, 而後這個生成的函數再將全部reducer
的結果合併成一個大的對象。promise
store是對以前說到一個聯繫和管理。具備以下職責
state
;getState()
方法獲取 statedispatch(action)
方法更新 state;subscribe(listener)
註冊監聽器;subscribe(listener)
返回的函數註銷監聽器。強調一下 Redux 應用只有一個單一的store
。當須要拆分數據處理邏輯時,你應該使用reducer
組合,而不是建立多個store
。store
的建立經過redux
的createStore
方法建立,這個方法還須要傳入reducer
,很容易理解:畢竟我須要dispatch
一個action
來改變state
嘛。 應用通常會有一個初始化的state
,因此可選爲第二個參數,這個參數一般是有服務端提供的,傳說中的Universal
渲染。 第三個參數通常是須要使用的中間件,經過applyMiddleware傳入。
說了這麼多,action
,store
,actionCreator
,reducer
關係就是這麼以下的簡單明瞭:
react-redux
,redux
和react
的橋樑工具。react-redux
將組建分紅了兩大類,UI組建component
和容器組建container
。 簡單的說,UI組建負責美的呈現,容器組件負責來幫你盛着,給你"力量"。
UI 組件有如下幾個特徵:
如:
export default class Counter extends Component{ render(){ const { counter, increment, decrement, incrementIfOdd, incrementAsync } = this.props; return( <p> Clicked:{counter} times <button onClick={increment}>+</button> <button onClick={decrement}>-</button> <button onClick={incrementIfOdd}>increment if Odd</button> <button onClick={incrementAsync}>increment async</button> </p> ) } }
容器組件特性則偏偏相反:
class App extends Component{ render(){ const { counter, increment, decrement, incrementIfOdd, incrementAsync } = this.props; return( <Counter counter={counter} increment={increment} decrement={decrement} incrementIfOdd={incrementIfOdd} incrementAsync={incrementAsync}/> ) } } export default connect( state=>({ counter: state.counter }), ActionCreators )(App);
connect
方法接受兩個參數:mapStateToProps
和mapDispatchToProps
。它們定義了UI組件的業務邏輯。前者負責輸入邏輯,即將state
映射到 UI 組件的參數(props), 後者負責輸出邏輯,即將用戶對 UI 組件的操做映射成Action
。由於做爲組件,咱們只要能拿到值,能發出改變值得action就能夠了,因此mapStateToProps
和mapDispatchToProps
正是知足這個需求的。
一個比較流行的redux的action中間件,它可讓actionCreator
暫時不返回action
對象,而是返回一個函數,函數傳遞兩個參數(dispatch, getState)
,在函數體內進行業務邏輯的封裝,好比異步操做,咱們至少須要觸發兩個action
,這時候咱們能夠經過redux-thunk
將這兩個action
封裝在一塊兒,以下:
const fetchDataAction = (querys) => (dispatch, getState) => { const setLoading = createAction('SET_LOADING'); dispatch(setLoading(true)); // 設置加載中。。。 return fetch(`${url}?${querys}`).then(r => r.json()).then(res => { dispatch(setLoading(false)); // 設置取消加載中。。。 dispatch(createAction('DATA_DO_SOMETHIN')(res)) }) }
這裏咱們的createCreator
返回的是一個fetch
對象,咱們下文會介紹,咱們經過dispatch
觸發改action
dispatch(fetchDataAction(querys))
在請求數據以前,經過redux-thunk
咱們能夠先觸發加載中的action
,等請求數據結束以後咱們能夠在次觸發action
,使得加載中狀態取消,並處理請求結果。
既然說到了異步action
,咱們可使用redux-promise
,它可讓actionCreator
返回一個Promise
對象。
第一種作法,咱們能夠參考redux-thunk
的部分。
第二種作法,action
對象的payload
屬性(至關於咱們的diy參數,action裏面攜帶的其餘參數)是一個Promise
對象。這須要從redux-actions
模塊引入createAction
方法,而且寫法也要變成下面這樣。
import { createAction } from 'redux-actions'; class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props // 發出異步 Action dispatch(createAction( 'FETCH_DATA', fetch(`url`).then(res => res.json()) )); }
其實redux-actions
的createAction
的源碼是拿到fetch對象的payload結果以後又觸發了一次action
。
當咱們的在開發大型應用的時候,對於大量的action
,咱們的reducer
須要些大量的swich來對action.type
進行判斷。redux-actions
能夠簡化這一煩瑣的過程,它能夠是actionCreator
,也能夠用來生成reducer
,其做用都是用來簡化action
、reducer
。
主要函數有createAction
、createActions
、handleAction
、handleActions
、combineActions
。
建立action
,參數以下
import { createAction } from 'redux-actions'; createAction( type, // action類型 payloadCreator = Identity, // payload數據 具體參考Flux教程 ?metaCreator // 具體我也沒深究是啥 )
例子以下:
export const increment = createAction('INCREMENT') export const decrement = createAction('DECREMENT') increment() // { type: 'INCREMENT' } decrement() // { type: 'DECREMENT' } increment(10) // { type: 'INCREMENT', payload: 10 } decrement([1, 42]) // { type: 'DECREMENT', payload: [1, 42] }
建立多個action
。
import { createActions } from 'redux-actions'; createActions( actionMap, ?...identityActions, )
第一個參數actionMap
爲一個對象,以action type
爲鍵值,值value有三種形式,
action
建立的時候傳入的參數,返回結果會做爲到生成的action
的payload
的value。payload
的值,第二個值也爲一個函數,返回meta
的值,不知道有什麼用。actionMap
對象,遞歸做用吧。例子以下
createActions({ ADD_TODO: todo => ({ todo }) REMOVE_TODO: [ todo => ({ todo }), // payloa (todo, warn) => ({ todo, warn }) // meta ] });
const actionCreators = createActions({ APP: { COUNTER: { INCREMENT: [ amount => ({ amount }), amount => ({ key: 'value', amount }) ], DECREMENT: amount => ({ amount: -amount }), SET: undefined // given undefined, the identity function will be used }, NOTIFY: [ (username, message) => ({ message: `${username}: ${message}` }), (username, message) => ({ username, message }) ] } }); expect(actionCreators.app.counter.increment(1)).to.deep.equal({ type: 'APP/COUNTER/INCREMENT', payload: { amount: 1 }, meta: { key: 'value', amount: 1 } }); expect(actionCreators.app.counter.decrement(1)).to.deep.equal({ type: 'APP/COUNTER/DECREMENT', payload: { amount: -1 } }); expect(actionCreators.app.counter.set(100)).to.deep.equal({ type: 'APP/COUNTER/SET', payload: 100 }); expect(actionCreators.app.notify('yangmillstheory', 'Hello World')).to.deep.equal({ type: 'APP/NOTIFY', payload: { message: 'yangmillstheory: Hello World' }, meta: { username: 'yangmillstheory', message: 'Hello World' } });
第二個參數identityActions
,可選參數,也是一個action type
吧,官方例子沒看懂,以下:
const { actionOne, actionTwo, actionThree } = createActions({ // function form; payload creator defined inline ACTION_ONE: (key, value) => ({ [key]: value }), // array form ACTION_TWO: [ (first) => [first], // payload (first, second) => ({ second }) // meta ], // trailing action type string form; payload creator is the identity }, 'ACTION_THREE'); expect(actionOne('key', 1)).to.deep.equal({ type: 'ACTION_ONE', payload: { key: 1 } }); expect(actionTwo('first', 'second')).to.deep.equal({ type: 'ACTION_TWO', payload: ['first'], meta: { second: 'second' } }); expect(actionThree(3)).to.deep.equal({ type: 'ACTION_THREE', payload: 3, });
字面意思理解,處理action
,那就是一個reducer
,包裹返回一個reducer
,處理一種類型的action type
。
import { handleAction } from 'redux-actions'; handleAction( type, // action類型 reducer | reducerMap = Identity defaultState // 默認state )
當第二個參數爲一個reducer處理函數時,形式以下,處理傳入的state
並返回新的state
:
handleAction('APP/COUNTER/INCREMENT', (state, action) => ({ counter: state.counter + action.payload.amount, }), defaultState);
當第二個參數爲reducerMap時,也爲處理state
並返回新的state
,只是必須傳入key值爲next
和throw
的兩個函數,分別用來處理state和異常以下:
handleAction('FETCH_DATA', { next(state, action) {...}, throw(state, action) {...}, }, defaultState);
官方推薦使用reducerMap
形式,由於與ES6的generator
相似。
與handleAction
不一樣,handleActions
能夠處理多個action
,也返回一個reducer
。
import { handleActions } from 'redux-actions'; handleActions( reducerMap, defaultState )
reducerMap
以action type
爲key,value與handleAction
的第二個參數一致,傳入一個reducer
處理函數或者一個只有next
和throw
兩個鍵值的對象。
另外,鍵值key也可使用createAction
建立:
import { createActions, handleActions } from 'redux-actions'; const { increment, decrement } = createActions({ 'INCREMENT': amount => ({ amount: 1 }), 'DECREMENT': amount => ({ amount: -1 }) }); const reducer = handleActions({ [increment](state, { payload: { amount } }) { return { counter: state.counter + amount } }, [decrement](state, { payload: { amount } }) { return { counter: state.counter + amount } } }, defaultState);
將多個action
或者actionCreator
結合起來,看起來不多用,具體例子以下:
const { increment, decrement } = createActions({ INCREMENT: amount => ({ amount }), DECREMENT: amount => ({ amount: -amount }) }); const reducer = handleActions({ [combineActions(increment, decrement)](state, { payload: { amount } }) { return { ...state, counter: state.counter + amount }; } }, { counter: 10 }); expect(reducer({ counter: 5 }, increment(5))).to.deep.equal({ counter: 10 }); expect(reducer({ counter: 5 }, decrement(5))).to.deep.equal({ counter: 0 }); expect(reducer({ counter: 5 }, { type: 'NOT_TYPE', payload: 1000 })).to.equal({ counter: 5 }); expect(reducer(undefined, increment(5))).to.deep.equal({ counter: 15 });
redux-actions
說到這裏,大概是這樣,有什麼不瞭解看看官方文檔吧。
Reselect
用來記憶selectors
的庫,咱們定義的selectors
是做爲函數獲取state
的某一部分。使用記憶能力,咱們能夠組織沒必要要的衍生數據的重渲染和計算過程,由此加速了咱們的應用。具體細節大概是在mapStateToProps
的時候,講state
的某一部分交給reselect
的selectors
來管理,使用selectors
的記憶功能讓組件的props
儘可能不變化,引發沒必要要的渲染。
下面咱們以一個todolist爲例子。
當咱們沒有reselect
的時候,咱們是直接經過mapStateToProps
把數據傳入組件內,以下。
const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } const mapStateToProps = (state, props) => { return { todolist: getVisibleTodos(state, props) } }
這個代碼有一個潛在的問題。每當state tree
改變時,selector
都要從新運行。當state tree
特別大,或者selector
計算特別耗時,那麼這將帶來嚴重的運行效率問題。爲了解決這個問題,reselect爲selector
設置了緩存,只有當selector
的輸入改變時,程序才從新調用selector函數。
這時咱們把state
轉化爲props
的數據交給reselect來
處理,咱們重寫mapStateToProps
。
const getVisibilityFilter = state => state.todo.showStatus const getTodos = state => state.todo.todolist const getVisibleTodos = createSelector([getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed) case 'SHOW_ACTIVE': return todos.filter(todo => !todo.completed) default: return todos } }) const mapStateToProps = (state, props) => { const todolist = getVisibleTodos(state, props); return { todolist } }
咱們使用createSelector
包裹起來,將組件內須要的兩個props
包裹起來,而後在返回一個獲取數據的函數getVisibleTodos
,這樣返回的todolist
就不會受到一些沒必要要的state的變化而變化引發衝渲染。
總結了那麼多的用法,其實也是redux
的基本用法,而後本身寫了半天的todolist
,把上面說到的技術都用了,這是 github地址,上面的內容若有錯誤,勿噴,畢竟入門級別。。。