既然要把 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 和 bluehandleSwitchColor 会根据传入的颜色 store.dispatch 一个 action 去修改颜色。

当然你现在点击按钮还是没有反应的。因为点击按钮的时候,只是更新 store 里面的 state,而并没有在 store.state 更新以后去重新渲染数据,我们其实就是忘了 store.subscribe 了。

给 Header.jsContent.jsThemeSwitch.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(一):初始化工程

动手实现 React-redux(二):结合 context 和 store的更多相关文章

  1. react redux 二次开发流程

    在一个大项目中如何引入redux及其相关技术栈(react-redux redux-thunk redux-immutable ),已经成为react前端工程师不可或缺的技能,下面通过实现一个简单的t ...

  2. React文档(二十二)context

    React中,通过React组件可以很容易地追踪数据流.当你关注一个组件,你可以发现哪一个props被传递了,这样使得你的应用很容被推断. 在一些情况下,你想要传递数据通过组件树而不需要去手动在每一层 ...

  3. react+redux教程(二)redux的单一状态树完全替代了react的状态机?

    上篇react+redux教程,我们讲解了官方计数器的代码实现,react+redux教程(一).我们发现我们没有用到react组件本身的state,而是通过props来导入数据和操作的. 我们知道r ...

  4. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  5. webpack+react+redux+es6

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  6. webpack+react+redux+es6开发模式---续

    一.前言 之前介绍了webpack+react+redux+es6开发模式 ,这个项目对于一个独立的功能节点来说是没有问题的.假如伴随着源源不断的需求,前段项目会涌现出更多的功能节点,需要独立部署运行 ...

  7. react + redux 完整的项目,同时写一下个人感悟

    先附上项目源码地址和原文章地址:https://github.com/bailicangd... 做React需要会什么? react的功能其实很单一,主要负责渲染的功能,现有的框架,比如angula ...

  8. Immutable.js 以及在 react+redux 项目中的实践

    来自一位美团大牛的分享,相信可以帮助到你. 原文链接:https://juejin.im/post/5948985ea0bb9f006bed7472?utm_source=tuicool&ut ...

  9. react+redux+react-redux练习项目

    一,项目目录 二.1.新建pages包,在pages中新建TodoList.js: 2.新建store包,在store包中新建store.js,reducer.js,actionCreater.js, ...

  10. React Redux Sever Rendering实战

    # React Redux Sever Rendering(Isomorphic JavaScript) ![React Redux Sever Rendering(Isomorphic)入门](ht ...

随机推荐

  1. C语言文件读写Demo

    CIODemo.c #include <stdio.h> #include <time.h> #define INPUT_BUFFER_SIZE 100 * 1024 int ...

  2. jQuery整理笔记九----功能性表格开发

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/hai_cheng001/article/details/27536965 演示样例中用到的一些图片. ...

  3. aop+自定义注解

    自定义注解,并且实现,需要两个文件: 自定义注解类: package com.clc.server.annotation; import java.lang.annotation.ElementTyp ...

  4. linux内存操作--ioremap和mmap

    最近在做视频输出相关的东西,对于预留给framebuffer的内存使用不是很清楚,现在找到一些资料整理一下,以备使用.if (想看使用方法)  goto   使用方法: 对于一个系统来讲,会有很多的外 ...

  5. vue 做登陆页面 ( 登陆成功后去掉注册和登陆按钮 显示用户名)

    1.当登陆成功  显示用户名且去掉登陆和注册按钮 2.data里声明  后  就在登陆的方法里调用啦 下面说一下登陆的方法. 1.登陆的按钮代码在第一张图片里 2.登陆的弹出框 3.方法 ps:另一种 ...

  6. html5--6-4 CSS选择器

    html5--6-4 CSS选择器 实例 学习要点 掌握常用的CSS选择器 了解不太常用的CSS选择器 什么是选择器 当我们定义一条样式时候,这条样式会作用于网页当中的某些元素,所谓选择器就是样式作用 ...

  7. hdu 4463 Outlets(最小生成树)

    题意:n个点修路,要求总长度最小,但是有两个点p.q必须相连 思路:完全图,prim算法的效率取决于节点数,适用于稠密图.用prim求解. p.q间距离设为0即可,最后输出时加上p.q间的距离 pri ...

  8. fuse的write过程到底是怎么样的,128KB的buffer怎么用?

    1. 在fuse/lib/fuse_kern_chan.c中有一个buffer设置 #define MIN_BUFSIZE 0x21000 //十进制132×1024  //为何不是128? 下面有一 ...

  9. [Selenium] common functions comparison

    1.Wait for element  in default time or self defined time When the element need some time to be prese ...

  10. AutoIt: send 命令 VS ControlClick的使用

    2008年的时候第一次接触AutoIt,当时觉得局限性太多了,就不想学,觉得把Watir,Ruby搞好就行了. 最近一段时间比较闲,发现自己对GUI的自动化操完全是短板,就把AutoIt重新拾起来了. ...