1. 單一數據源html
all states ==>Store
react
2. 單向數據流git
dispatch(actionCreator) => Reducer => (state, action) => state
github
// actionType export const ACTION_TYPE = 'ACTION_TYPE'; // actionCreator let actionCreator = (config) => { return { type: ACTION_TYPE, // 必須定義 type config // 傳遞參數 => reducer } }
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducers from '../reducers'; let store = createStore( reducers, applyMiddleware(thunk) );
// 接收方法 let receiveSomething = (res) => { return { type: RECEIVE_SOME, res } } // 獲取數據方法 export let fetchSomething = (args) => { return dispatch => { return fetch(args).then((res) => { return dispatch(receiveSomething(res)) }) } }
(initialState, action) => newState
import { ACTION_A, ACTION_B } from '../actions'; let initialState = { ... } function example(state = initialState, action) { switch(action.type) { case ACTION_A: return Object.assign({}, state, action.config) case ACTION_B: return Object.assign({}, state, action.config) } }
let doSomething = (config) => { let { a, b } = config; // do something with a, b return { a, b } } function example(state = initialState, action) { switch(action.type) { case ACTION_TYPE: return Object.assign({}, state, doSomething(action.config)) } }
經過 redux 提供的 combineReducers 將不一樣處理邏輯的 reducer 合併起來。redux
import { combineReducers } from 'redux'; export default combineReducers({ reducerA, reducerB }); // or export let reducer = (state = initialState, action) { a: processA(state.a, action), b: processB(state.b, action) }
使用 react-redux 提供的 Provider 能夠將 Store 注入到 react 中。api
Store 將合併後的 reducers 經過 createStore 建立,此外下面示例代碼還使用中間件加入了一層 react-thunk 處理。app
import ReactDOM from 'react-dom'; import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux' import thunk from 'redux-thunk'; import reducers from './reducers'; let store = createStore( reducers, applyMiddleware(thunk) ); ReactDOM.render(( <Provider store={store}> // ... </Provider> ), document.querySelector('#app'));
使用 react-redux 提供的 connect 方法 將組件和所需數據綁定。dom
須要注意的是,Store 建立時接收的是合併後的 reducers, 所以不一樣 reducer 上的處理數據綁定在了不一樣 reducer 對象上,而不是所有掛載在 Store 上。異步
mapStateToProps 將組件內部所需數據經過 props 傳入組件內部。更多綁定機制,具體可參考connectide
import React, { Component } from 'react'; import { connect } from 'react-redux'; class ComponentA extends Component { //... } let mapStateToProps = (state) => { // attention !!! let { reducerA, reducerB } = state; return { propA: reducerA.propA, propB: reducerB.propB } }; export default connect(mapStateToProps)(ComponentA);
React bindings for Redux embrace the idea of separating presentational and container components.
Redux 的 React 綁定庫包含了 容器組件和展現組件相分離 的開發思想。
展現型組件和容器型組件的區別在官方文檔中已經給出很詳細的解釋了,可是中文文檔的翻譯有誤,因此直接看英文比較更容易懂。
Presentational Components | Container Components | |
---|---|---|
Purpose | How things look (markup, styles) | How things work (data fetching, state updates) |
Aware of Redux | No | Yes |
To read data | Read data from props | Subscribe to Redux state |
To change data | Invoke callbacks from props | Dispatch Redux actions |
Are written | By hand | Usually generated by React Redux |
組件類型區分的模糊點在於怎麼界定組件的內部功能規劃。若是斷定一個組件爲展現型組件,那麼它所需數據和處理方法都應該從父級傳入,保持組件內部「純淨」。
在實際開發中,一個組件的邏輯跟業務緊密相關。若是須要將數據和方法從外部傳入,那麼父級組件所作的事情會不少,多重的子組件也會把父級邏輯弄亂,這就不是 redux 的初衷了。
中文文檔翻譯的意思是:容器組件應該爲路由層面的組件,但這樣既不符合實際開發須要,也違背了 redux 思想。真正界定兩種組件的因素是:
當組件 connect 後,dispatch 方法已經注入到 props 中,因此觸發 Action 能夠從 props 獲取 dispatch 方法。
import React, { Component } from 'react'; // actionCreator import { actionA, actionB } from 'actions/actionA' class ComponentA extends Component { constructor(props) { super(props); } componentDidMount() { let { dispatch } = this.props; dispatch(actionA()) } } export default connect()(ComponentA);
組件內部所需的渲染數據都已經綁定在了 props 上,直接獲取便可。
須要注意的是,在事件監聽中觸發 Action,須要用一個匿名函數封裝,不然 React 在渲染時就會執行事件綁定事件,而不是當事件發生再執行。
render() { let { dispatch, propA, propB } = this.props; return ( <section> // Attention !!! <input type="text" onClick={(ev) => dispatch(actionB(ev))} /> <p className={propA}>{propB}</p> </section> ) }
容器型組件須要鏈接 Redux,使用 dispatch 觸發 actionCreator。
展現型組件須要用到的方法調用在容器型組件內定義好,經過 props 傳入到展現型組件中。
// get actionCreator import { actionA } from './actions/actionA'; class Parent extends Component { handleCallback(data) { // use dispatch let { dispatch } = this.props; dispatch(actionA(data)); } render() { return ( <Child onSomethingChange={this.handleCallback} /> ) } } // connet Redux export default connect()(Parent);
展現型組件不須要用到 Redux 的一切,它的 props 僅僅存在於父級傳入的數據和方法。
// don't need action/dispatch/connect class Child extends Component { handleSomething(data) { // handle anything with props this.props.onSomethingChange(data); } render() { return ( // just markup & style <input onChange={handleSomething} /> ) } }
圖示箭頭表明各概念之間的相互關係,不表明數據流。( 能理解下面這張圖,這篇文章就沒白看了 -。- )
參考文檔
END.