redux解决的核心问题是父子兄弟等组件件传值很麻烦的问题,因而有了一个"通信班"--redux,这个通信班能够帮咱们把组件之间的状态整合到一块儿,而后修改也统一修改。咱们以为很nice。上一篇文章我简单的跟你们解读了redux的工做原理。可是,修改完成之后,把修改的东西再通知下去咱们又会以为是一个麻烦,咱们也但愿能有这样一个通信班来帮咱们把命令传达下去。为了解决这个问题,react-redux义无反顾的出现了。这样相对来讲就比较完美了。那今天咱们就会想把整个工做流都本身来简单实现了,也便有了接下来的故事。redux、react-redux兄弟同心,齐力传值。css
redux三板斧,store、action,reducer。 以前简单了作了一个redux。今天,把上次的代码优化下,作个精致的redux,可是其实里面的东西都是差很少的。在这一部分的分享我也不会作太详细的讲解,若是过程当中有疑问能够看下上篇文章,或者看完后不懂你们能够留言互相交流。 createStore建立的对象拥有4个API,他们分管不一样的职能。
react
export const createStore = (state,storeChange) => {
const listeners = [];
let store = state || {};
const subscribe = (listener) => {
listeners.push(listener)
}
const dispatch = (action) => {
const newStore = storeChange(store,action);
store = newStore;
listeners.forEach((item) => item())
}
const getStore = () => {
return store;
}
return {store,dispatch,subscribe,getStore}
}
复制代码复制代码
subcribe使用订阅发布者模式。组件订阅了,中央有改变的时候就会发布消息给他。订阅的方式,经过一个监听数组,把每一个组件的render函数都放到有个数组里面,当数据改变后,咱们把全部订阅了的组件的监听函数从新执行一遍。这样视图层就获得了改变。所以在dispatch的设计思想就是,先把reducer后的值拿过来,把它赋值给中央,再把组件的值更新下。 storeChange.js的代码,使用es6解构语法以下:es6
export const storeChange = (store,action) => {
switch (action.type) {
case "HEAD":
return {
...store,
head: action.head
}
case "BODY":
return {
...store,
body:action.body
}
default:
return { ...store}
}
}
复制代码复制代码
reudx在大型项目中每每会有不少的输出,所以咱们在此也用了一个设计模式,把输出统一,这样便于后期的维护和修改。index.js代码以下:redux
export * from './createStore';
export * from './storeChange';
复制代码复制代码
好了。来到今天的重磅嘉宾了。你以为是react-redux。固然不是。而是react-redux的中流砥柱,context。设计模式
Context是React的高级API ,使用context能够实现跨组件传值。在react-redux的中就是经过context提供一个全局的store ,拖拽组件的react-dnd,经过Context在组件中分发DOM的Drg和Drop事件。不只如此,路由组件react-router还能够经过Context管理路由状态等等,能够说至关重量级的嘉宾了。 这是官方的一个描述: 数组
Context的使用基于生产者消费者模式。
父节点做为Context的生产者,而消费者则是父节点下的全部的节点。父节点经过一个静态属性childContextTypes提供给子组件的Context对象属性,并实现一个实例getChildCotext方法,返回一个Context纯对象。而子组件经过一个静态属性contextTypes声明后,才能访问父组件的context对象属性,不然即便属性名没有写错,拿到的对象也是undefined。 App.js咱们代码设计以下:bash
import React, { Component } from "react";
import PropTypes from "prop-types"
import Body from "./component/body/Body"
import Head from "./component/head/Head"
import { createStore, storeChange} from './redux';
// import './App.css';
class App extends Component {
static childContextTypes = {
store: PropTypes.object,
dispatch: PropTypes.func,
subscribe: PropTypes.func,
getStore: PropTypes.func
}
getChildContext() {
const state = {
head: "我是全局head",
body: "我是全局body",
headBtn: "修改head",
bodyBtn: "修改body"
}
const { store,dispatch, subscribe,getStore } = createStore(state,storeChange)
return { store,dispatch,subscribe,getStore}
}
render() {
return (
<div className="App">
<Head />
<Body />
</div>
);
}
}
export default App;
复制代码复制代码
static声明一个ChildContextTypes,顶层组件规定要传给子组件Context对象的属性类型,一个getChildContext函数,返回给子组件一个纯对象 ,子组件中接收,子组件目录结构以下: react-router
import React, {Component} from 'react';
import Button from '../Button/Button';
import PropTypes from "prop-types";
export default class Body extends Component {
static contextTypes = {
store: PropTypes.object,
subscribe: PropTypes.func,
getStore: PropTypes.func
}
constructor(props) {
super(props);
this.state = {};
}
componentWillMount () {
const { subscribe } = this.context;
this._upState();
subscribe(()=> this._upState())
}
_upState() {
const { getStore } = this.context;
this.setState({
...getStore()
})
}
render () {
return (
<div>
<div className="body">{this.state.body}</div>
<Button/>
</div>
)
}
}
复制代码复制代码
子组件经过一个contextTypes,用来接收父组件传过来的属性。组件中使用了状态就表明他须要订阅,所以咱们加载组件的时候就就组件的setState方法push到监听函数里面,这样就能让数据改变后页面可以获得从新渲染。关于setState这篇文章--setState这个API到底怎么样讲解的挺详细的,不是很明白setState背后的原理的骨子能够看看。 同理贴上Head.js:svg
import React, {Component} from 'react';
import PropTypes from "prop-types"
export default class Head extends Component{
static contextTypes = {
store: PropTypes.object,
subscribe: PropTypes.func,
getStore: PropTypes.func
}
constructor(props) {
super(props);
this.state = { };
}
componentWillMount () {
const { subscribe } = this.context;
this._upState();
subscribe(()=> this._upState())
}
_upState() {
const { getStore } = this.context;
this.setState({
...getStore()
})
}
render() {
return (
<div className="head">{this.state.head}</div>
)
}
}
复制代码复制代码
Button.js函数
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class Button extends Component {
static contextTypes = {
store: PropTypes.object,
dispatch: PropTypes.func,
subscribe: PropTypes.func,
getStore: PropTypes.func
}
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {
this._upState();
}
_upState() {
const { store } = this.context;
this.setState({
...store
})
}
changeContext(type) {
const { dispatch } =this.context;
const key = type === "HEAD" ? "head":"body";
dispatch({
type: type,
[key]: `我是修改后的${key}`
})
}
render () {
return (
<div className="button">
<div className="btn" onClick={() => {
this.changeContext("HEAD")
}}>改变head</div>
<div className="btn" onClick={() => {
this.changeContext("BODY")
}}>改变body</div>
</div>
)
}
}
复制代码复制代码
整个流程走完,context的API在react-redux的用法就是这样的了 这是效果:
context总共分为四步: