动手实现 React-redux(二):结合 context 和 store
既然要把 store 和 context 结合起来,我们就先构建 store。在 src/index.js 加入之前创建的 createStore 函数,并且构建一个 themeReducer 来生成一个 store:
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:
class Index extends Component {
  static childContextTypes = {
    store: PropTypes.object
  }
  getChildContext () {
    return { store }
  }
  render () {
    return (
      <div>
        <Header />
        <Content />
      </div>
    )
  }
}
如果有些同学已经忘记了 context 的用法,可以参考之前的章节: React.js 的 context 。
然后修改 src/Header.js,让它从 Index 的 context 里面获取 store,并且获取里面的 themeColor 状态来设置自己的颜色:
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。
然后在 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:
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>
    )
  }
}
这时候,主题已经完全生效了,整个页面都是红色的:

当然现在点按钮还是没什么效果,我们接下来给按钮添加事件。其实也很简单,监听 onClick 事件然后 store.dispatch 一个 action 就好了,修改 src/ThemeSwitch.js:
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 的第一次胜利会师,当然还有很多需要优化的地方。
下一节:动手实现 React-redux(三):connect 和 mapStateToProps
动手实现 React-redux(二):结合 context 和 store的更多相关文章
- react redux  二次开发流程
		在一个大项目中如何引入redux及其相关技术栈(react-redux redux-thunk redux-immutable ),已经成为react前端工程师不可或缺的技能,下面通过实现一个简单的t ... 
- React文档(二十二)context
		React中,通过React组件可以很容易地追踪数据流.当你关注一个组件,你可以发现哪一个props被传递了,这样使得你的应用很容被推断. 在一些情况下,你想要传递数据通过组件树而不需要去手动在每一层 ... 
- react+redux教程(二)redux的单一状态树完全替代了react的状态机?
		上篇react+redux教程,我们讲解了官方计数器的代码实现,react+redux教程(一).我们发现我们没有用到react组件本身的state,而是通过props来导入数据和操作的. 我们知道r ... 
- webpack+react+redux+es6开发模式
		一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ... 
- webpack+react+redux+es6
		一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ... 
- webpack+react+redux+es6开发模式---续
		一.前言 之前介绍了webpack+react+redux+es6开发模式 ,这个项目对于一个独立的功能节点来说是没有问题的.假如伴随着源源不断的需求,前段项目会涌现出更多的功能节点,需要独立部署运行 ... 
- react + redux 完整的项目,同时写一下个人感悟
		先附上项目源码地址和原文章地址:https://github.com/bailicangd... 做React需要会什么? react的功能其实很单一,主要负责渲染的功能,现有的框架,比如angula ... 
- Immutable.js 以及在 react+redux 项目中的实践
		来自一位美团大牛的分享,相信可以帮助到你. 原文链接:https://juejin.im/post/5948985ea0bb9f006bed7472?utm_source=tuicool&ut ... 
- react+redux+react-redux练习项目
		一,项目目录 二.1.新建pages包,在pages中新建TodoList.js: 2.新建store包,在store包中新建store.js,reducer.js,actionCreater.js, ... 
- React Redux Sever Rendering实战
		# React Redux Sever Rendering(Isomorphic JavaScript)  @end @implementation ViewContr ... 
- Linux 命令行命令及参数辨异
			1. 软链接与硬链接 ln -s 源文件 目标文件 当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在其它的目 ... 
- [Selenium] Android 中旋转屏幕,触摸,滚动
			package com.learingselenium.android; import junit.framework.TestCase import org.openqa.selenium.Rota ... 
- 【CAIOJ 1178】 最长共同前缀长度
			[题目链接] 点击打开链接 [算法] EXKMP [代码] #include<bits/stdc++.h> using namespace std; #define MAXL 100001 ... 
- NOIP2008 传纸条(DP及滚动数组优化)
			传送门 这道题有好多好多种做法呀……先说一下最暴力的,O(n^4的做法) 我们相当于要找两条从左上到右下的路,使路上的数字和最大.所以其实路径从哪里开始走并不重要,我们就直接假设全部是从左上出发的好啦 ... 
- AndroidStudio修改主题外观和字体大小
			修改主题外观 File --> Settings --> Appearance & Behavior --> Appearance 右边 Theme 修改编辑器的字体大小 F ... 
- 【旧文章搬运】Windbg+Vmware驱动调试入门(二)---Vmware及GuestOS的设置
			原文发表于百度空间,2009-01-08========================================================================== 这一篇是主 ... 
- Educational Codeforces Round 21 D - Array Division (前缀和+二分)
			传送门 题意 将n个数划分为两块,最多改变一个数的位置, 问能否使两块和相等 分析 因为我们最多只能移动一个数x,那么要么将该数往前移动,要么往后移动,一开始处理不需要移动的情况 那么遍历sum[i] ... 
- java 发送get,post请求
			package wzh.Http; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStr ... 
