經過 Redux架構理解 咱們瞭解到 Redux
架構的 store
、action
、reducers
這些基本概念和工做流程。咱們也知道了 Redux
這種架構模式能夠和其餘的前端庫組合使用,而 React-redux
正是把 Redux
這種架構模式和 React.js
結合起來的一個庫。前端
在 React
應用中,數據是經過 props
屬性自上而下進行傳遞的。若是咱們應用中的有不少組件須要共用同一個數據狀態,能夠經過狀態提高的思路,將共同狀態提高到它們的公共父組件上面。可是咱們知道這樣作是很是繁瑣的,並且代碼也是難以維護的。這時會考慮使用 Context
,Context
提供了一個無需爲每層組件手動添加 props
,就能在組件樹間進行數據傳遞的方法。也就是說在一個組件若是設置了 context
,那麼它的子組件均可以直接訪問到裏面的內容,而不用經過中間組件逐級傳遞,就像一個全局變量同樣。react
在 App
-> Toolbar
-> ThemedButton
使用 props
屬性傳遞 theme
,Toolbar
做爲中間組件將 theme
從 App
組件 傳遞給 ThemedButton
組件。redux
class App extends React.Component { render() { return <Toolbar theme="dark" />; } } function Toolbar(props) { // Toolbar 組件接受一個額外的「theme」屬性,而後傳遞給 ThemedButton 組件。 // 若是應用中每個單獨的按鈕都須要知道 theme 的值,這會是件很麻煩的事, // 由於必須將這個值層層傳遞全部組件。 return ( <div> <ThemedButton theme={props.theme} /> </div> ); } class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; } }
使用 context
,就能夠避免經過中間元素傳遞 props
了segmentfault
// Context 可讓咱們無須明確地傳遍每個組件,就能將值深刻傳遞進組件樹。 // 爲當前的 theme 建立一個 context(「light」爲默認值)。 const ThemeContext = React.createContext('light'); class App extends React.Component { render() { // 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。 // 不管多深,任何組件都能讀取這個值。 // 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。 return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } // 中間的組件不再必指明往下傳遞 theme 了。 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class ThemedButton extends React.Component { // 指定 contextType 讀取當前的 theme context。 // React 會往上找到最近的 theme Provider,而後使用它的值。 // 在這個例子中,當前的 theme 值爲 「dark」。 static contextType = ThemeContext; render() { return <Button theme={this.context} />; } }
雖然解決了狀態傳遞的問題卻引入了 2 個新的問題。性能優化
context
就像全局變量同樣,裏面的數據能夠被子組件隨意更改,可能會致使程序不可預測的運行。context
極大地加強了組件之間的耦合性,使得組件的複用性變差,好比 ThemedButton
組件由於依賴了 context
的數據致使複用性變差。咱們知道,redux
不正是提供了管理共享狀態的能力嘛,咱們只要經過 redux
來管理 context
就能夠啦,第一個問題就能夠解決了。微信
React-Redux
提供 Provider
組件,利用了 react
的 context
特性,將 store
放在了 context
裏面,使得該組件下面的全部組件都能直接訪問到 store
。大體實現以下:架構
class Provider extends Component { // getChildContext 這個方法就是設置 context 的過程,它返回的對象就是 context,全部的子組件均可以訪問到這個對象 getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object }
那麼咱們能夠這麼使用,將 Provider
組件做爲根組件將咱們的應用包裹起來,那麼整個應用的組件均可以訪問到裏面的數據了app
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import todoApp from './reducers'; import App from './components/App'; const store = createStore(todoApp); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
還記得咱們的第二個問題嗎?組件由於 context
的侵入而變得不可複用。React-Redux
爲了解決這個問題,將全部組件分紅兩大類:展現組件和容器組件。dom
展現組件
展現組件有幾個特徵ide
UI
的展現,沒有任何業務邏輯this.state
props
決定Redux
的 API
展現組件就和純函數同樣,返回結果只依賴於它的參數,而且在執行過程裏面沒有反作用,讓人以爲很是的靠譜,能夠放心的使用。
import React, { Component } from 'react'; import PropTypes from 'prop-types'; class Title extends Component { static propTypes = { title: PropTypes.string } render () { return ( <h1>{ this.props.title }</h1> ) } }
像這個 Title
組件就是一個展現組件,組件的結果徹底由外部傳入的 title
屬性決定。
容器組件
容器組件的特徵則相反
UI
展現Redux state
獲取Redux
的 API
你能夠直接使用 store.subscribe()
來手寫容器組件,可是不建議這麼作,由於這樣沒法使用 React-redux
帶來的性能優化。
React-redux
規定,全部的展現組件都由用戶提供,容器組件則是由 React-Redux 的 connect()
自動生成。
React-redux
提供 connect
方法,能夠將咱們定義的展現組件生成容器組件。connect
函數接受一個展現組件參數,最後會返回另外一個容器組件回來。因此 connect
實際上是一個高階組件(高階組件就是一個函數,傳給它一個組件,它返回一個新的組件)。
import { connect } from 'react-redux'; import Header from '../components/Header'; export default connect()(Header);
上面代碼中,Header
就是一個展現組件,通過 connect
處理後變成了容器組件,最後把它導出成模塊。這個容器組件沒有定義任何的業務邏輯,全部不能作任何事情。咱們能夠經過 mapStateToProps
和 mapDispatchToProps
來定義咱們的業務邏輯。
import { connect } from 'react-redux'; import Title from '../components/Title'; const mapStateToProps = (state) => { return { title: state.title } } const mapDispatchToProps = (dispatch) => { return { onChangeColor: (color) => { dispatch({ type: 'CHANGE_COLOR', color }); } } } export default connect(mapStateToProps, mapDispatchToProps)(Title);
mapStateToProps
告訴 connect
咱們要取 state
裏的 title
數據,最終 title
數據會以 props
的方式傳入 Title
這個展現組件。
mapStateToProps
還會訂閱 Store
,每當 state
更新的時候,就會自動執行,從新計算展現組件的參數,從而觸發展現組件的從新渲染。
mapDispatchToProps
告訴 connect
咱們須要 dispatch action
,最終 onChangeColor
會以 props
回調函數的方式傳入 Title
這個展現組件。
Connect
組件大概的實現以下
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { allProps: {} } } componentWillMount () { const { store } = this.context this._updateProps() store.subscribe(() => this._updateProps()) } _updateProps () { const { store } = this.context let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) // 將 Store 的 state 和容器組件的 state 傳入 mapStateToProps : {} // 判斷 mapStateToProps 是否傳入 let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) // 將 dispatch 方法和容器組件的 state 傳入 mapDispatchToProps : {} // 判斷 mapDispatchToProps 是否傳入 this.setState({ allProps: { ...stateProps, ...dispatchProps, ...this.props } }) } render () { // 將 state.allProps 展開以容器組件的 props 傳入 return <WrappedComponent {...this.state.allProps} /> } } return Connect }
至此,咱們就很清楚了,原來 React-redux
就是經過 Context
結合 Redux
來實現 React
應用的狀態管理,經過 Connect
這個高階組件來實現展現組件和容器組件的鏈接的。
更多精彩內容,歡迎關注微信公衆號~