React的上下文-Context

開場白

19年的第一篇文章,雖然18年也沒有分享多少,可是19年開始,我以爲要好好學習,好好努力。固然新的一年伊始,祝你們在19年平安、幸福,還有發發發。css

導語

redux解決的核心問題是父子兄弟等組件件傳值很麻煩的問題,因而有了一個"通信班"--redux,這個通信班能夠幫咱們把組件之間的狀態整合到一塊兒,而後修改也統一修改。咱們以爲很nice。上一篇文章我簡單的跟你們解讀了redux的工做原理。可是,修改完成之後,把修改的東西再通知下去咱們又會以爲是一個麻煩,咱們也但願能有這樣一個通信班來幫咱們把命令傳達下去。爲了解決這個問題,react-redux義無反顧的出現了。這樣相對來講就比較完美了。那今天咱們就會想把整個工做流都本身來簡單實現了,也便有了接下來的故事。redux、react-redux兄弟同心,齊力傳值。vue

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解構語法以下:git

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代碼以下:es6

export *  from './createStore';
export * from './storeChange';
複製代碼

好了。來到今天的重磅嘉賓了。你以爲是react-redux。固然不是。而是react-redux的中流砥柱,context。github

有請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咱們代碼設計以下:redux

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函數,返回給子組件一個純對象 ,子組件中接收,子組件目錄結構以下: 設計模式

子組件目錄結構
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:數組

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獲取數據

後期React對Context作了調整,可是更方便咱們使用,有須要的能夠看下個人github上demo.js,一清二楚。

結束語

其實一步步慢慢去了解,就能發現萬事萬物真的皆是一理,以前面對兩個主流框架,react和vue,你們都說vue更簡單,更容易上手,因而就先學了vue,剛學習的時候,感受,好像也不難,後面在公司實習的時候,一直用,感受用的挺順手的。就以爲若是要用起來也就那麼回事。再到離職後,發現都在用react,去學react,也是一樣的感受。學習多是一個坎,堅持下跨過去了就行了。一個大三老油條[hahah]如今也是邊準備春招的實習面試,邊學習,邊寫文章。分享不到位或是不正確的地方望你們指正。

相關文章
相關標籤/搜索