Web 應用是一個狀態機,視圖與狀態是一一對應的javascript
全部的狀態,保存在一個對象裏面java
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]; }
在實際項目中,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)
咱們使用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。
redux將全部組件分爲UI組件和容器組件。
UI 組件有如下幾個特徵。
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。
原理圖:
<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>