React上下文-Context

导语

redux解决的核心问题是父子兄弟等组件件传值很麻烦的问题,因而有了一个"通信班"--redux,这个通信班能够帮咱们把组件之间的状态整合到一块儿,而后修改也统一修改。咱们以为很nice。上一篇文章我简单的跟你们解读了redux的工做原理。可是,修改完成之后,把修改的东西再通知下去咱们又会以为是一个麻烦,咱们也但愿能有这样一个通信班来帮咱们把命令传达下去。为了解决这个问题,react-redux义无反顾的出现了。这样相对来讲就比较完美了。那今天咱们就会想把整个工做流都本身来简单实现了,也便有了接下来的故事。redux、react-redux兄弟同心,齐力传值。css

redux三板斧

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

Context是React的高级API ,使用context能够实现跨组件传值。在react-redux的中就是经过context提供一个全局的store ,拖拽组件的react-dnd,经过Context在组件中分发DOM的Drg和Drop事件。不只如此,路由组件react-router还能够经过Context管理路由状态等等,能够说至关重量级的嘉宾了。 这是官方的一个描述: 数组

图片关于context的描述

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

子组件目录结构
Body.js

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

context总共分为四步:

  • ChildContextTypes => 顶层组件中规定类型
  • getChildContext 顶层组件中设置传递属性
  • 后代组件经过contextTypes 规定数据类型
  • 后代组件this.context获取数据
相关文章
相关标签/搜索