preface

Context is translated as Context, and in the programming world, this is a concept you often encounter, including React.

Context is classified as Advanced in the React API, but it is not recommended for stable versions of the App.

The vast majority of applications do not need to use content.

If you want your application to be stable, don’t use context. It is an experimental API and it is likely to break in future releases of React.

However, that doesn’t mean we don’t need to focus on the Context. In fact, many good React components use the Context to perform their functions. For example, the of react-redux provides a global store using the Context. Drag and drop the react-dnd component, Context is used to distribute DOM Drag and Drop events in components. The Route component React-Router uses Context to manage route state, and so on. In React component development, if you use Context well, you can make your components powerful and flexible.

Today I want to talk to you a little bit about the Context that I’ve come to recognize in development, and how I’ve used it for component development.

Note: All apps mentioned in this article refer to Web apps.

I met the React Context

The official definition of Context

The React document website does not define a Context. It describes the Context used and how to use it.

The official website describes the Context scenario as follows:

In Some Cases, you want to pass data through the component tree without having to pass the props down manuallys at every level. you can do this directly in React with the powerful “context” API.

Simply put, when you don’t want to pass props or state layer by layer in the component tree, you can use Context to pass component data across layers.

Pass data using props or state, and the data flows from the top.

Using Context, you can pass data across components.

How to use Context

For Context to come into play, there are two components, the Context producer, which is usually a parent, and the Consumer of a Context, which is usually one or more child nodes. So the use of Context is based on the producer-consumer pattern.

The parent component, the Context producer, declares the properties of the Context object provided to the child component through a static property childContextTypes and implements an instance getChildContext method, Return a plain object representing the Context.

import React from 'react'
import PropTypes from 'prop-types'

class MiddleComponent extends React.Component {
  render () {
    return <ChildComponent />}} Class ParentComponent extends React.Component {// Declare Context object property static childContextTypes = {propA: Proptypes.string, methodA: proptypes.func} // getChildContext () {return {propA: proptypes.string, methodA: proptypes.func} 'propA', methodA: () => 'methodA' } } render () { return<MiddleComponent />}}Copy the code

For consumers of the Context, access the Context provided by the parent component in the following way.

import React from 'react'
import PropTypes from 'prop-types'

class ChildComponent extends React.Component {
  // Declare the Context property to use
  static contextTypes = {
    propA: PropTypes.string
  }
  
  render () {
    const {
      propA,
      methodA
    } = this.context
    
    console.log(`context.propA = ${propA}`)  // context.propA = propA
    console.log(`context.methodA = ${methodA}`)  // context.methodA = undefined
    
    return. }}Copy the code

Child components need to be declared with a static contextTypes property to access the properties of the parent Context object. Otherwise, the object is undefined, even if the property name is correctly written.

For Stateless Component, you can access the parent’s Context in the following way

import React from 'react'
import PropTypes from 'prop-types'

const ChildComponent = (props, context) = > {
  const {
    propA
  } = context
    
  console.log(`context.propA = ${propA}`)  // context.propA = propA
    
  return. } ChildComponent.contextProps = {propA: PropTypes.string    
}
Copy the code

In subsequent releases React tweaked the Context API to make the producer-consumer model more explicit.

import React from 'react';
import ReactDOM from 'react-dom';

const ThemeContext = React.createContext({
  background: 'red'.color: 'white'
});
Copy the code

Create a Context object using the static method react.createcontext (). This Context object contains two components, and
.

class App extends React.Component {
  render () {
    return (
      <ThemeContext.Provider value={{background: 'green', color: 'white'}} >
        <Header />
      </ThemeContext.Provider>); }}Copy the code

The value of is equivalent to the current getChildContext().

class Header extends React.Component {
  render () {
    return (
      <Title>Hello React Context API</Title>); }}class Title extends React.Component {
  render () {
    return (
      <ThemeContext.Consumer>
        {context => (
          <h1 style={{background: context.background.color: context.color}} >
            {this.props.children}
          </h1>
        )}
      </ThemeContext.Consumer>); }}Copy the code

The children of
must be a function that takes the Context provided by as an argument to the function.

Context’s new API is much more React style.

A couple of places you can get the Context directly

In fact, in addition to the instance context property (this.context), the React component has several places to directly access the context provided by the parent component. For example, constructor:

  • constructor(props, context)

Such as the life cycle:

  • componentWillReceiveProps(nextProps, nextContext)
  • shouldComponentUpdate(nextProps, nextState, nextContext)
  • componetWillUpdate(nextProps, nextState, nextContext)

For function-oriented stateless components, the component Context can be accessed directly through the parameters of the function.

const StatelessComponent = (props, context) = >(...)Copy the code

This is the basis for Context, but more detailed guidance can be found here

My understanding of Context

OK, after the basics, let’s talk about my understanding of the React Context.

Think of Context as a component scope

React App developers know that a React App is essentially a tree of React components. Each React component is equivalent to a node in the tree. In addition to the root node of the App, each node has a parent component chain.

For example, the parent chain of
is


, and the parent chain of
is
, and the parent chain of
has only one component Node. It’s
.

The tree of connected component nodes actually forms a Context tree. Each node’s Context comes from the Context object provided by getChildContext(), which is a combination of all component nodes in the parent chain.

Have know JS scope chain concept developer should all know, JS code block during execution, will create a corresponding scope chain, the scope chain recording the runtime JS code block during execution can access the activities of the object, including variables and functions, JS program through the scope chain access to the code block variables and functions of the internal or external.

Using the JS chain of scopes as an analogy, the React component provides the Context as a scope for child components to access, and the properties of the Context can be thought of as live objects in the scope. Since a component’s Context is composed of the Context objects returned by all components in the parent chain via getChildContext(), a component can access the properties of the Context provided by all components in the parent chain.

So, I borrowed the JS scope chain idea and used the Context as the scope of the component.

Focus on the controllability and scope of Context

However, the concept of Context as a component scope is different from the common concept of scope (in terms of the programming languages I personally work with so far). We need to focus on the controllability and scope of Context.

It’s common, natural, and even insensitive to use a scope or Context in your development. However, using a Context in React is not that easy. The parent component that provides the Context needs to be “declared” by childContextTypes, and the child component that uses the parent’s Context property needs to be “claimed” by contextTypes. Therefore, I consider the React Context to be a “privileged” component scope **.

What are the benefits of this “with permissions” approach? From my personal understanding, the first is to keep the framework API consistent and use the declarative coding style as propTypes does. In addition, you can ensure some degree of controllability and scope of the Context provided by the component.

React App components are a tree structure that extends layer by layer. Parent and child components are linearly dependent on each other. Arbitrary use of Context can actually break this dependency, resulting in unnecessary additional dependencies between components, reducing component reusability, and potentially affecting App maintainability.

As you can see, the
component has dependencies on both
and
because the Child uses the parent’s Context. Once separated from these two components, the availability of
is not guaranteed, reducing the reusability of
.

In my opinion, exposing data or apis through Context is not an elegant practice, although react-Redux does this. Therefore, a mechanism, or constraint, is needed to reduce unnecessary impact.

The constraints of the static properties childContextTypes and contextTypes ensure that only the component itself or any other child component associated with the component can access the Context’s properties, whether data or functions. Because only the component itself or related child components can know which Context properties it can access, as opposed to other components that are not related to the component, internal or external, because it is not clear what Context properties are “declared” by childContextTypes of each parent component in the parent chain. Therefore, there is no way to “apply” the associated attribute via contextTypes. Giving a component’s scoped Context “permission” ensures that the Context is controlled and scoped to some extent.

When developing components, we should always be aware of this and not use the Context arbitrarily.

You don’t have to use Context first

As the React advanced API, React does not recommend using Context as a priority. My understanding is:

  • ContextIt is still in the experimental stage and may be subject to major changes in future releases. In fact, this has already happened, so it is not recommended to use in the App in order to avoid major impact and trouble in future upgradesContext.
  • Although not recommended for use in appsContextHowever, for components, as the influence range is smaller than App, it can still be considered if high cohesion can be achieved without destroying the dependency of component treeContext.
  • For data communication between components or state management, this is preferredpropsorstateSolution, and then consider using other third-party mature library solution, the above method is not the best choice, then consider usingContext.
  • ContextThe update needs to passsetState()Trigger, but it’s not reliable.ContextCross-component access is supported, however, if the intermediate child component does not respond to updates by some means, such asshouldComponentUpdate()returnfalseThen there is no guaranteeContextThe update must be accessible for useContextChild component of. As a result,ContextReliability needs attention. However, the update issue is resolved in the new VERSION of the API.

In short, it’s okay to use a Context as long as you make sure it’s controllable, and even more so if used properly, Context can actually be a powerful experience for React component development.

Use Context as the medium for sharing data

Context can be used to communicate data across components. And I think of it as, like, a bridge, as a medium for data sharing. There are two types of data sharing: app-level and component-level.

  • App-level data sharing

The Context object provided by the App root component can be thought of as the global scope at the App level. Therefore, we use the Context object provided by the App root component to create some global data at the App level. For example, see React-redux. Here is the core implementation of the component source code:

export function createProvider(storeKey = 'store', subKey) {
    const subscriptionKey = subKey || `${storeKey}Subscription`

    class Provider extends Component {
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null}}constructor(props, context) {
          super(props, context)
          this[storeKey] = props.store;
        }

        render() {
          return Children.only(this.props.children)
        }
    }

    / /...

    Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
    }
    Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
    }

    return Provider
}

export default createProvider()
Copy the code

When the root component of an App is wrapped with the component, it essentially provides a global property store for the App, which is equivalent to sharing store properties across the App. Of course, a component can also be wrapped in other components, sharing stores globally at the component level.

  • Component-level data sharing

If the functionality of a component cannot be accomplished by the component itself and requires additional child components, you can use Context to build a component composed of multiple child components. For example, react-router.

The < router /> of the React-Router cannot operate and manage routes independently, because navigation links and jumps are usually separated. Therefore, the and
subcomponents are required to complete routes together. In order to enable related sub-components to work together, the react-Router implementation uses Context to share a router among related components such as < router />, , and
, thus achieving unified operation and management of routes.

To better understand the above, let’s extract the source code of
, , and
components.

// Router.js

/** * The public API for putting history on context. */
class Router extends React.Component {
  static propTypes = {
    history: PropTypes.object.isRequired,
    children: PropTypes.node
  };

  static contextTypes = {
    router: PropTypes.object
  };

  static childContextTypes = {
    router: PropTypes.object.isRequired
  };

  getChildContext() {
    return {
      router: {
        ...this.context.router,
        history: this.props.history,
        route: {
          location: this.props.history.location,
          match: this.state.match
        }
      }
    };
  }
  
  / /...
  
  componentWillMount() {
    const { children, history } = this.props;
    
    / /...
    
    this.unlisten = history.listen((a)= > {
      this.setState({
        match: this.computeMatch(history.location.pathname)
      });
    });
  }

  / /...
}
Copy the code

Although there is other logic in the source code, the essence of
isto provide a Context with Router attributes for the child components, listen for history, and if history changes, trigger component rerendering via setState().

// Link.js

/** * The public API for rendering a history-aware . */
class Link extends React.Component {
  
  / /...
  
  static contextTypes = {
    router: PropTypes.shape({
      history: PropTypes.shape({
        push: PropTypes.func.isRequired,
        replace: PropTypes.func.isRequired,
        createHref: PropTypes.func.isRequired
      }).isRequired
    }).isRequired
  };

  handleClick = event= > {
    if (this.props.onClick) this.props.onClick(event);

    if (
      !event.defaultPrevented &&
      event.button === 0&&!this.props.target && ! isModifiedEvent(event) ) { event.preventDefault();// Use the Router instance provided by the 
       component
      const { history } = this.context.router;
      const { replace, to } = this.props;

      if (replace) {
        history.replace(to);
      } else{ history.push(to); }}}; render() {const{ replace, to, innerRef, ... props } =this.props;

    // ...

    const { history } = this.context.router;
    const location =
      typeof to === "string"
        ? createLocation(to, null.null, history.location)
        : to;

    const href = history.createHref(location);
    return (
      <a {. props} onClick={this.handleClick} href={href} ref={innerRef} />); }}Copy the code

The core of isto render the tag, intercept the click event of the tag, and then route history through the shared Router of
to notify
to re-render.

// Route.js

/** * The public API for matching a single path and rendering. */
class Route extends React.Component {
  
  / /...
  
  state = {
    match: this.computeMatch(this.props, this.context.router)
  };

  // Computes the matching path, returns a matching object if it matches, and null otherwise
  computeMatch(
    { computedMatch, location, path, strict, exact, sensitive },
    router
  ) {
    if (computedMatch) return computedMatch;
    
    / /...

    const { route } = router;
    const pathname = (location || route.location).pathname;
    
    return matchPath(pathname, { path, strict, exact, sensitive }, route.match);
  }
 
  / /...

  render() {
    const { match } = this.state;
    const { children, component, render } = this.props;
    const { history, route, staticContext } = this.context.router;
    const location = this.props.location || route.location;
    const props = { match, location, history, staticContext };

    if (component) return match ? React.createElement(component, props) : null;

    if (render) return match ? render(props) : null;

    if (typeof children === "function") return children(props);

    if(children && ! isEmptyChildren(children))return React.Children.only(children);

    return null; }}Copy the code

has a part of the source code similar to
, which can implement routing nesting, but its core is to determine whether the Router shared by the Context matches the path of the current Route, and then render components.

The react-router is built around a < router /> Context.

Develop components using Context

Previously, we developed a simple component with Context, slot distribution components. This chapter discusses how to use Context for component development using this slot distribution experience.

Slot distribution component

Let’s start with what a slot distribution component is, a concept that was first introduced in Vuejs. Slot distribution is a technique of inserting the content of a parent component into a child component template through composition of components, called Slot in Vuejs.

To give you a more intuitive understanding of this concept, I carried a Demo of slot distribution from Vuejs.

For the supplied slot component
, the template is as follows:

<div>
  <h2>I am the title of the child component</h2>
  <slot>Only displayed if there is no content to distribute</slot>
</div>
Copy the code

For the parent component, the template is as follows:

<div>
  <h1>I am the title of the parent component</h1>
  <my-component>
    <p>So here's some initial stuff</p>
    <p>This is more initial stuff</p>
  </my-component>
</div>
Copy the code

Final render result:

<div>
  <h1>I am the title of the parent component</h1>
  <div>
    <h2>I am the title of the child component</h2>
    <p>So here's some initial stuff</p>
    <p>This is more initial stuff</p>
  </div>
</div>
Copy the code

You can see that the
node of the component < my-Component /> is eventually replaced by something under the
node of the parent component.

Vuejs also supports named slots.

For example, a layout component
:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
Copy the code

And in the parent component template:

<app-layout>
  <h1 slot="header">This might be a page title</h1>
  <p>A paragraph of the main content.</p>
  <p>Another paragraph.</p>
  <p slot="footer">Here's some contact information</p>
</app-layout>
Copy the code

Final render result:

<div class="container">
  <header>
    <h1>This might be a page title</h1>
  </header>
  <main>
    <p>A paragraph of the main content.</p>
    <p>Another paragraph.</p>
  </main>
  <footer>
    <p>Here's some contact information</p>
  </footer>
</div>
Copy the code

The advantage of slot distribution is that it allows components to be abstractable into templates. The component itself only cares about the template structure, leaving the details to the parent component without breaking the syntax that HTML uses to describe the DOM structure. I think it’s a good technology, but React isn’t very friendly to support it. Therefore, I developed a set of slot distribution components based on React by referring to Vuejs’ slot distribution component. The React component can also be templated.

For the
component, I’d like it to look like this:

class AppLayout extends React.Component {
  static displayName = 'AppLayout'
  
  render () {
    return (
      <div class="container">
        <header>
          <Slot name="header"></Slot>
        </header>
        <main>
          <Slot></Slot>
        </main>
        <footer>
          <Slot name="footer"></Slot>
        </footer>
      </div>)}}Copy the code

For outer use, it can be written like this:

<AppLayout> <AddOn slot="header"> <h1> Here may be a page title </h1> </AddOn> <AddOn> <p> a paragraph of the main content. </p> <p> Another paragraph. </p> </AddOn> <AddOn slot="footer"> <p> Here is some contact information </p> </AddOn> </AppLayout>Copy the code

Component implementation ideas

Based on the previous thoughts, let’s organize the implementation ideas.

As you can see, the Slot distribution component relies on two child components — the Slot component
and the distribution component
. Slot component, responsible for piling, provide distribution of content pit. The distribution component, which collects distribution content and supplies it to the slot component to render distribution content, acts as a consumer of the slot.

Obviously, there is a problem here. The
component is separate from the
component. How do you fill the contents of
into
? It is not difficult to solve this problem. Two independent modules need to be connected, so build a bridge between them. So how do you build this bridge? Go back and look at the code you envisioned earlier.

For the
component, you want it to look like this:

class AppLayout extends React.Component {
  static displayName = 'AppLayout'
  
  render () {
    return (
      <div class="container">
        <header>
          <Slot name="header"></Slot>
        </header>
        <main>
          <Slot></Slot>
        </main>
        <footer>
          <Slot name="footer"></Slot>
        </footer>
      </div>)}}Copy the code

For outer use, it is written like this:

<AppLayout> <AddOn slot="header"> <h1> Here may be a page title </h1> </AddOn> <AddOn> <p> a paragraph of the main content. </p> <p> Another paragraph. </p> </AddOn> <AddOn slot="footer"> <p> Here is some contact information </p> </AddOn> </AppLayout>Copy the code

Both
and
are actually scoped by
.
is the component node returned by the
component render() method, and
is the children node of
, so, You can think of
as the role that Bridges
and
. So, how does
connect
to
? Here we use the main character of this article, Context. The next question is, how do you use Context to connect
to
?

The
bridge was mentioned earlier. In the outer component,
is responsible for collecting the content for the slot filling through
.
itself defines an interface to get the fill content using the Context. When rendering,
is the node of
rendering, so
can use the Context to get the interface defined by
to get the fill content for rendering.

Implement slot distribution components according to the idea

Since
is the children node of
and
is a specific component that we can recognize by name or displayName,
before rendering, Cache each
child with the slot value as the key before render() ‘s return. If
is not set to slot, then it is treated as if it is filling the non-named < slot />. We can give these non-named slots a key, such as? Default.

For
, the code looks like this:

class AppLayout extends React.Component {
  
  static childContextTypes = {
    requestAddOnRenderer: PropTypes.func
  }
  
  // Used to cache the contents of each 
      
  addOnRenderers = {}
  
  // Provide interfaces to child nodes through Context
  getChildContext () {
    const requestAddOnRenderer = (name) = > {
      if (!this.addOnRenderers[name]) {
        return undefined
      }
      return (a)= > (
        this.addOnRenderers[name]
      )
    }
    return {
      requestAddOnRenderer
    }
  }

  render () {
    const{ children, ... restProps } =this.props

    if (children) {
      // Cache the contents of 
       in k-v mode
      const arr = React.Children.toArray(children)
      const nameChecked = []
      this.addOnRenderers = {}
      arr.forEach(item= > {
        const itemType = item.type
        if (item.type.displayName === 'AddOn') {
          const slotName = item.props.slot || '? default'
          // Make sure the content is unique
          if (nameChecked.findIndex(item= >item === stubName) ! = =- 1) {
            throw new Error(`Slot(${slotName}) has been occupied`)}this.addOnRenderers[stubName] = item.props.children
          nameChecked.push(stubName)
        }
      })
    }

    return (
      <div class="container">
        <header>
          <Slot name="header"></Slot>
        </header>
        <main>
          <Slot></Slot>
        </main>
        <footer>
          <Slot name="footer"></Slot>
        </footer>
      </div>)}}Copy the code

defines a Context interface, the requestAddOnRenderer() interface returns a function based on name, This returned function accesses the properties of addOnRenderers, which is the content cache object for
, based on name.

The implementation of
is simple, with the following code:

// props, context
const Slot = ({ name, children }, { requestAddOnRenderer }) = > {
  const addOnRenderer = requestAddOnRenderer(name)
  return (addOnRenderer && addOnRenderer()) ||
    children ||
    null
}

Slot.displayName = 'Slot'
Slot.contextTypes = { requestAddOnRenderer: PropTypes.func }
Slot.propTypes = { name: PropTypes.string }
Slot.defaultProps = { name: '? default' }
Copy the code

gets the requestAddOnRenderer() interface from the context provided by
. The main object to render is the
content cached in
. If the specified
content is not retrieved, the children of
itself are rendered.

simpler:

const AddOn = (a)= > null

AddOn.propTypes = { slot: PropTypes.string }
AddOn.defaultTypes = { slot: '? default' }
AddOn.displayName = 'AddOn'
Copy the code

does nothing but return null, which makes
cache the contents distributed to the slot.

You can make<AppLayout />More universal

is basically a component with slot distribution capability, but it is clear that
is not universal, so we can upgrade it to a stand-alone universal component.

I named this component SlotProvider

function getDisplayName (component) {
  return component.displayName || component.name || 'component'
}

const slotProviderHoC = (WrappedComponent) = > {
  return class extends React.Component {
    static displayName = `SlotProvider(${getDisplayName(WrappedComponent)}) `

    static childContextTypes = {
      requestAddOnRenderer: PropTypes.func
    }
  
    // Used to cache the contents of each 
      
    addOnRenderers = {}
  
    // Provide interfaces to child nodes through Context
    getChildContext () {
      const requestAddOnRenderer = (name) = > {
        if (!this.addOnRenderers[name]) {
          return undefined
        }
        return (a)= > (
          this.addOnRenderers[name]
        )
      }
      return {
        requestAddOnRenderer
      }
    }

    render () {
      const{ children, ... restProps } =this.props

      if (children) {
        // Cache the contents of 
       in k-v mode
        const arr = React.Children.toArray(children)
        const nameChecked = []
        this.addOnRenderers = {}
        arr.forEach(item= > {
          const itemType = item.type
          if (item.type.displayName === 'AddOn') {
            const slotName = item.props.slot || '? default'
            // Make sure the content is unique
            if (nameChecked.findIndex(item= >item === stubName) ! = =- 1) {
              throw new Error(`Slot(${slotName}) has been occupied`)}this.addOnRenderers[stubName] = item.props.children
            nameChecked.push(stubName)
          }
        })
      }
      
      return (<WrappedComponent {. restProps} / >)
    }
  }
}

export const SlotProvider = slotProviderHoC
Copy the code

React uses higher-level components to transform the original
into a standalone, general-purpose component. For the original
, you can use this SlotProvider higher-order component to convert it into a component with slot distribution capability.

import { SlotProvider } from './SlotProvider.js'

class AppLayout extends React.Component {
  static displayName = 'AppLayout'
  
  render () {
    return (
      <div class="container">
        <header>
          <Slot name="header"></Slot>
        </header>
        <main>
          <Slot></Slot>
        </main>
        <footer>
          <Slot name="footer"></Slot>
        </footer>
      </div>)}}export default SlotProvider(AppLayout)
Copy the code

As you can see from the above experience, when designing and developing a component,

  • Components may require a root component and children to work together to accomplish component functionality. For example, the slot distribution component actually needsSlotProviderwith<Slot />and<AddOn />When used together,SlotProviderAs the root component, while<Slot />and<AddOn />They are all child components.
  • The position of the child components relative to the root component or between the child components is uncertain. forSlotProviderIn terms of<Slot />The position of theta is indeterminate, it will be at the point of beingSlotProviderAny location in the template of the component wrapped by this higher-order component, while for<Slot />and<AddOn />Their immediate location is also uncertain. One is inSlotProviderPackaging the components inside, another isSlotProviderthechildren.
  • Subcomponents need to rely on some global API or data between them, such as<Slot />The actual rendered content comes fromSlotProviderThe collected<AddOn />The content of the.

This is where we need an intermediary to share the data. It is more elegant to use the Context directly than to introduce additional third-party modules such as Redux.

Try the new version of the Context API

Revamp the previous slot distribution component using a new version of the Context API.

// SlotProvider.js

function getDisplayName (component) {
  return component.displayName || component.name || 'component'
}

export const SlotContext = React.createContext({
  requestAddOnRenderer: (a)= >{}})const slotProviderHoC = (WrappedComponent) = > {
  return class extends React.Component {
    static displayName = `SlotProvider(${getDisplayName(WrappedComponent)}) `

    // Used to cache the contents of each 
      
    addOnRenderers = {}
  
    requestAddOnRenderer = (name) = > {
      if (!this.addOnRenderers[name]) {
        return undefined
      }
      return (a)= > (
        this.addOnRenderers[name]
      )
    }

    render () {
      const{ children, ... restProps } =this.props

      if (children) {
        // Cache the contents of 
       in k-v mode
        const arr = React.Children.toArray(children)
        const nameChecked = []
        this.addOnRenderers = {}
        arr.forEach(item= > {
          const itemType = item.type
          if (item.type.displayName === 'AddOn') {
            const slotName = item.props.slot || '? default'
            // Make sure the content is unique
            if (nameChecked.findIndex(item= >item === stubName) ! = =- 1) {
              throw new Error(`Slot(${slotName}) has been occupied`)}this.addOnRenderers[stubName] = item.props.children
            nameChecked.push(stubName)
          }
        })
      }
      
      return( <SlotContext.Provider value={ requestAddOnRenderer: this.requestAddOnRenderer }> <WrappedComponent {... restProps} /> </SlotContext.Provider> ) } } } export const SlotProvider = slotProviderHoCCopy the code

The previous childContextTypes and getChildContext() have been removed. Other than local adjustments, the overall core remains the same.

// Slot.js

import { SlotContext } from './SlotProvider.js'

const Slot = ({ name, children }) = > {
  return (
    <SlotContext.Consumer>
      {(context) => {
        const addOnRenderer = requestAddOnRenderer(name)
          return (addOnRenderer && addOnRenderer()) ||
            children ||
            null
      }}
    </SlotContext.Consumer>
  )
}

Slot.displayName = 'Slot'
Slot.propTypes = { name: PropTypes.string }
Slot.defaultProps = { name: '?default' }
Copy the code

Since the Context was previously used in a producer-consumer fashion, and the components themselves are relatively simple, the transformation using the new API makes little difference.

conclusion

  • Compared with thepropsandstate, the ReactContextComponent communication can be achieved across hierarchies.
  • The use of the Context API is based on the producer-consumer pattern. Producer side, via component static propertieschildContextTypesDeclare, and then pass the instance methodgetChildContext()createContextObject. On the consumer side, through component static propertiescontextTypesIt’s for the applicationContextProperty, and then through the instancecontextaccessContextProperties.
  • useContextIt requires a bit of thought and is not recommended for use in appsContext, but if the development process of components can ensure the cohesion of components, controllable and maintainable, does not destroy the dependency of the component tree, the impact range is small, it can be consideredContextFix some problems.
  • throughContextExposing API may bring convenience to solve some problems to some extent, but I personally think it is not a good practice and should be cautious.
  • The old versionContextThe update of the dependencysetState()Is unreliable, but this problem is fixed in the new VERSION of the API.
  • Can put theContextThink of it as the scope of a component, but with concernContextBefore use, analyze whether it is really necessary to use and avoid some side effects caused by overuse.
  • Can put theContextUse as a medium for App – or component-level data sharing.
  • Design and develop a component, if the component needs to be associated with multiple components, useContextMaybe it could be more elegant.

The above is my share content, if there is insufficient or wrong place, welcome criticism and correction.

reference

  • Context – https://reactjs.org/docs/context.html
  • The React to the 16.3: with a new Context API – http://cnodejs.org/topic/5a7bd5c4497a08f571384f03
  • Content Distribution with Slots – https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots
  • Pictured above – http://www.mafengwo.cn/poi/36580.html