既然要把 store 和 context 結合起來,咱們就先構建 store。在 src/index.js
加入以前建立的 createStore
函數,而且構建一個 themeReducer
來生成一個 store
:css
import React, { Component } from 'react' import PropTypes from 'prop-types' import ReactDOM from 'react-dom' import Header from './Header' import Content from './Content' import './index.css' function createStore (reducer) { let state = null const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { state = reducer(state, action) listeners.forEach((listener) => listener()) } dispatch({}) // 初始化 state return { getState, dispatch, subscribe } } const themeReducer = (state, action) => { if (!state) return { themeColor: 'red' } switch (action.type) { case 'CHANGE_COLOR': return { ...state, themeColor: action.themeColor } default: return state } } const store = createStore(themeReducer) ...
themeReducer
定義了一個表示主題色的狀態 themeColor
,而且規定了一種操做 CHNAGE_COLOR
,只能經過這種操做修改顏色。如今咱們把 store
放到 Index
的 context 裏面,這樣每一個子組件均可以獲取到 store
了,修改 src/index.js
裏面的 Index
:html
class Index extends Component { static childContextTypes = { store: PropTypes.object } getChildContext () { return { store } } render () { return ( <div> <Header /> <Content /> </div> ) } }
若是有些同窗已經忘記了 context 的用法,能夠參考以前的章節: React.js 的 context 。react
而後修改 src/Header.js
,讓它從 Index
的 context 裏面獲取 store
,而且獲取裏面的 themeColor
狀態來設置本身的顏色:redux
class Header extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { themeColor: '' } } componentWillMount () { this._updateThemeColor() } _updateThemeColor () { const { store } = this.context const state = store.getState() this.setState({ themeColor: state.themeColor }) } render () { return ( <h1 style={{ color: this.state.themeColor }}>React.js 小書</h1> ) } }
其實也很簡單,咱們在 constructor
裏面初始化了組件本身的 themeColor
狀態。而後在生命週期中 componentWillMount
調用 _updateThemeColor
,_updateThemeColor
會從 context 裏面把 store
取出來,而後經過 store.getState()
獲取狀態對象,而且用裏面的 themeColor
字段設置組件的 state.themeColor
。dom
而後在 render
函數裏面獲取了 state.themeColor
來設置標題的樣式,頁面上就會顯示:函數
如法炮製 Content.js
:優化
class Content extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { themeColor: '' } } componentWillMount () { this._updateThemeColor() } _updateThemeColor () { const { store } = this.context const state = store.getState() this.setState({ themeColor: state.themeColor }) } render () { return ( <div> <p style={{ color: this.state.themeColor }}>React.js 小書內容</p> <ThemeSwitch /> </div> ) } }
還有 src/ThemeSwitch.js
:this
class ThemeSwitch extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { themeColor: '' } } componentWillMount () { this._updateThemeColor() } _updateThemeColor () { const { store } = this.context const state = store.getState() this.setState({ themeColor: state.themeColor }) } render () { return ( <div> <button style={{ color: this.state.themeColor }}>Red</button> <button style={{ color: this.state.themeColor }}>Blue</button> </div> ) } }
這時候,主題已經徹底生效了,整個頁面都是紅色的:spa
固然如今點按鈕仍是沒什麼效果,咱們接下來給按鈕添加事件。其實也很簡單,監聽 onClick
事件而後 store.dispatch
一個 action
就行了,修改 src/ThemeSwitch.js
:code
class ThemeSwitch extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { themeColor: '' } } componentWillMount () { this._updateThemeColor() } _updateThemeColor () { const { store } = this.context const state = store.getState() this.setState({ themeColor: state.themeColor }) } // dispatch action 去改變顏色 handleSwitchColor (color) { const { store } = this.context store.dispatch({ type: 'CHANGE_COLOR', themeColor: color }) } render () { return ( <div> <button style={{ color: this.state.themeColor }} onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button> <button style={{ color: this.state.themeColor }} onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button> </div> ) } }
咱們給兩個按鈕都加上了 onClick
事件監聽,並綁定到了 handleSwitchColor
方法上,兩個按鈕分別給這個方法傳入不一樣的顏色 red
和 blue
,handleSwitchColor
會根據傳入的顏色 store.dispatch
一個 action
去修改顏色。
固然你如今點擊按鈕仍是沒有反應的。由於點擊按鈕的時候,只是更新 store
裏面的 state
,而並無在 store.state
更新之後去從新渲染數據,咱們其實就是忘了 store.subscribe
了。
給 Header.js
、Content.js
、ThemeSwitch.js
的 componentWillMount
生命週期都加上監聽數據變化從新渲染的代碼:
... componentWillMount () { const { store } = this.context this._updateThemeColor() store.subscribe(() => this._updateThemeColor()) } ...
經過 store.subscribe
,在數據變化的時候從新調用 _updateThemeColor
,而 _updateThemeColor
會去 store
裏面取最新的 themeColor
而後經過 setState
從新渲染組件,這時候組件就更新了。如今能夠自由切換主題色了:
咱們順利地把 store 和 context 結合起來,這是 Redux 和 React.js 的第一次勝利會師,固然還有不少須要優化的地方。