React 將頁面元素拆分紅組件,經過組裝展現數據。組件又有無狀態和有狀態之分,所謂狀態,能夠簡單的認爲是組件要展現的數據。React 有個特性或者說是限制單向數據流,組件的狀態數據只能在組件內部修改,對於其餘組件是只讀的,想要修改只能經過組件提供的接口回調。html
隨着組件數量的增多,組件間狀態數據共享的複雜性也會隨之增長,若是僅使用 React 組件內的 State 可能會致使程序的流程混亂,代碼難以維護。react
原文連接:https://www.chuonye.com/archives/react-redux.html數據庫
爲何這麼說呢,咱們來看一個圖:redux
原始的 React,當最底層組件須要改變數據時,若是數據在父組件,回調方法層層傳遞便可;若是在兄弟組件,那就須要藉助一箇中間組件,固然了也有辦法直接與兄弟組件通訊。能夠想象,隨着應用複雜程度的提升,組件間通訊使用的各類直接或間接回調,可能會致使代碼亂成一團。antd
此時,Redux 就派上用場了,如上圖所示,它把應用的全部狀態-數據存儲在一個地方,並稱之爲Store,組件間不直接通訊,而是把變化的數據推給 Store,須要根據狀態變化從新渲染的組件經過訂閱 Store 來實現。框架
Redux 在設計與實現時,遵循三大原則或者說規範限制。dom
整個應用程序的狀態數據僅存儲在一個 Store 中,數據就存儲在一個大的對象樹(object tree)中。ide
惟一的 Store 也是隻讀的,應用程序沒法直接修改狀態數據。Store 對象自己的 API 很是少,僅有四個方法:函數
getState()
: 獲取當前狀態數據dispatch(action)
: 推送觸發變化subscribe(listener)
: 訂閱數據變化replaceReducer(nextReducer)
顯而易見,沒有提供設置狀態的方法。其中的 dispatch(action)
是惟一改變數據的方法,好比:工具
var action = { type: 'ADD_USER', user: {name: 'chuonye.com'} }; store.dispatch(action);
dispatch
方法會將 action
傳遞給 Redux,action 就是一個普通對象,包含觸發的操做類型以及數據。
Redux 接收到 action
後,會使用一個純函數來處理,這些函數被稱爲 Reducers:
var someReducer = function(state, action) { ... return state; }
Reducer 接收當前的 state 和 action 做爲參數,它也不能直接修改原有的數據,而是經過返回一個新的 state 來修改。
總的來講,Redux 是一個幫助應用統一管理狀態數據的工具,它遵循嚴格的單向數據流(Store 只讀)設計,使得應用的行爲變得可預測且容易理解。
Redux 通常和 React 這類框架搭配使用,爲了方便與 React 集成,Redux 官方提供了一個 react-redux 綁定庫。react-redux 將組件劃分爲容器組件,UI組件和其餘組件,其中:
從總體來看,在使用上,應用代碼分層設計,結構以下:
咱們不妨結合着數據庫,來理解下每一個層次的意思,從下往上:
這些設計怎麼有點熟悉,跟 Java 是愈來愈像了。下面簡單寫個例子,看看到底怎麼用。
接下來咱們按照使用原生 React,使用 Redux 和使用 React-Redux的順序分別實現一個 Counter 計數器的功能。
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class Counter extends Component { constructor(props) { super(props); this.state = { value: 0 }; // 狀態數據 } render() { return ( <div> <p>state: {this.state.value}</p> <button onClick={() => this.handleIncrement()}>+1</button> <button onClick={() => this.handleDecrement()}>-1</button> </div> ); } // 處理方法 handleIncrement() { let curVal = this.state.value + 1; this.setState({value: curVal}); } handleDecrement() { let curVal = this.state.value - 1; this.setState({value: curVal}); } } // 渲染 ReactDOM.render(<Counter />, document.getElementById('root'))
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; // 接收 action 並處理,簽名:(state, action) => newState function reducer(state = {value: 0}, action) { let curVal = state.value; switch (action.type) { case 'INCREMENT': return {value: curVal+1}; case 'DECREMENT': return {value: curVal-1}; default: return state; } } // 建立 Redux store const store = createStore(reducer); const render = () => ReactDOM.render( <div> <p>state: {store.getState().value}</p> {/* dispatch 通知 store 改變數據 */} <button onClick={()=>{store.dispatch({type: 'INCREMENT'})}}>+1</button> <button onClick={()=>{store.dispatch({type: 'DECREMENT'})}}>-1</button> </div>, document.getElementById('root') ) render(); // 訂閱 store,狀態改變時更新 UI store.subscribe(render);
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; import { Provider, connect } from 'react-redux'; // 接收 action 並處理 function reducer(state = {value: 0}, action) { let curVal = state.value; switch (action.type) { case 'INCREMENT': return {value: curVal+1}; case 'DECREMENT': return {value: curVal-1}; default: return state; } } // 定義一個 UI 組件,用於展現數據 class CounterUI extends Component { render() { // 數據來源於 props const {value, handleIncrement, handleDecrement} = this.props; return ( <div> <p>state: {value}</p> <button onClick={handleIncrement}>+1</button> <button onClick={handleDecrement}>-1</button> </div> ); } } // 定義一個 容器組件,用於與 Redux store 交互 // 將 Redux state 按需注入到 UI 組件的 props function mapStateToProps(state) { return { value: state.value } } // 將 Redux actions 按需注入到 UI 組件的 props function mapDispatchToProps(dispatch) { return { handleIncrement: () => dispatch({type: 'INCREMENT'}), handleDecrement: () => dispatch({type: 'DECREMENT'}) } } // 使用 connect 方法基於 UI組件 生成一個 容器組件 const CounterContainer = connect(mapStateToProps, mapDispatchToProps)(CounterUI); const store = createStore(reducer); // 使用 Provider 做爲根組件,使得全部子組件都能訪問到 store ReactDOM.render( <Provider store={store}> <CounterContainer /> </Provider>, document.getElementById('root') )
Redux 和 React-Redux 專門設計了約束和約定,致使代碼量不見得減小,甚至還可能增長了。簡單的項目看不出有什麼優點,但在構建必定規模的企業級項目時,這些約束和條條框框,會有利於項目的維護和管理。正如 Java,有些人認爲過於囉嗦,但這正是它嚴謹的體現!
下一篇將會結合 antd UI框架模仿實現官方提供的購物車實例。