The official website describes this third-party package as Ponyfill for React. CreateContext (which is compatible with lower versions of React). Comparison with create-react-context:

original mini
install size 50 kB 140 kB
minified 3.3 kB 2.3 kB
minzip 1.3 kB 1.0 kB

Outdated ReactContext API

Source code analysis

src/index.ts

import React from 'react';
import createReactContext from './implementation';

CreateContext is exported by default. When React. CreateContext does not exist (earlier versions of React), createReactContext is exported
export default React.createContext || createReactContext;
Copy the code

src/index.d.ts

Type of file

import * as React from 'react';

// The first parameter is the default value and calculateChangedBits is used to control the rendering of the subscription component
export default function createReactContext<T> (
	defaultValue: T,
	calculateChangedBits?: (prev: T, next: T) => number
) :Context<T>;

// Consumer takes a function as a child and returns a ReactNode
type RenderFn<T> = (value: T) = > React.ReactNode;

Context contains a Provider for publishing and a Consumer for subscribing
export type Context<T> = {
	Provider: React.ComponentClass<ProviderProps<T>>;
	Consumer: React.ComponentClass<ConsumerProps<T>>;
};

// The Provider props accepts a value and children
export type ProviderProps<T> = {
	value: T; children? : React.ReactNode; observedBits? : any, };// Consumer props accepts a children of type RenderFn, and observedBits are used to control the rendering of the Consumer child component
export type ConsumerProps<T> = {
	children: RenderFn<T> | [RenderFn<T>]; observedBits? : number; };Copy the code

src/implementation.ts

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import warning from 'tiny-warning';

/ / convert hexadecimal 2 = 111111111111111111111111111111
const MAX_SIGNED_31_BIT_INT = 1073741823;

// globalThis's polyfill is used to access the current global context
const commonjsGlobal: any =
// The globalThis standard attribute is used to access global objects in the current environment
    typeofglobalThis ! = ='undefined' // 'global proper'
    ? globalThis
	// Browser environment, return window object
    : typeof window! = ='undefined' 
        ? window // Browser
		// nodejs environment, return global
        : typeof global! = ='undefined'
            ? global // node.js
            : {}

// Get a value named __global_unique_id__ from the global context
function getUniqueId() {
    const key = '__global_unique_id__';
    return commonjsGlobal[key] = (commonjsGlobal[key] || 0) + 1;
}

// Inlined Object.is polyfill.
// Object. Is polyfill, React. CreateContext update comparison is implemented through the Object
// The main difference with congruence === is that + 0-0 is not equal, NaN and NaN are equal. == is first converted to the number type when comparing different types
/ / this code from the MDN, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
function objectIs(x: any, y: any) {
	if (x === y) {
		// Return true if neither x nor y is 0
		// If x and y are both 0 and the same sign, return true
		// 1/-0 -Infinity 1/+0 Infinity
		// For + 0-0 check
		returnx ! = =0 || 1 / x === 1 / y;
	} else {
		// If x and y are NaN, return true. Used for NaN and NaN verification
		returnx ! == x && y ! == y; }}// Declare the ConsumerState type, which contains a value attribute
export type ConsumerState<T> = {
	value: T
};

// Declare the RenderFn type, accept a value property and return a ReactNode
type RenderFn<T> = (value: T) = > React.ReactNode;

// Declare Context, containing Provider and Consumer ReactComponent properties
export type Context<T> = {
	Provider: React.ComponentClass<ProviderProps<T>>;
	Consumer: React.ComponentClass<ConsumerProps<T>>;
};

// Declare the type of props received by the Provider
export type ProviderProps<T> = {
	value: T; children? : React.ReactNode; observedBits? : any, };// Declare the type of props that the Consumer receives
export type ConsumerProps<T> = {
	children: RenderFn<T> | [RenderFn<T>]; observedBits? : number; };// Create an EventEmitter function
function createEventEmitter(value: any) {
	let handlers: any[] = [];
	return {
		// Collect events
		on(handler: any) {
			handlers.push(handler);
		},
		// Cancel the event
		off(handler: any) {
			handlers = handlers.filter(h= >h ! == handler); },// Get value
		get() {
			return value;
		},
		// Trigger the currently registered listener function when setting value
		set(newValue: any, changedBits: any) {
			value = newValue;
			handlers.forEach(handler= >handler(value, changedBits)); }}; }// Determine if children is an array, if so, return only the first child. In the Consumer
function onlyChild(children: any) :any {
	return Array.isArray(children) ? children[0] : children;
}

// calculateChangedBits is used to control whether to render or not, and when the return value is 0, the subscribed child components are not rendered
export default function createReactContext<T> (defaultValue: T, calculateChangedBits? : (a: T, b: T) => number) :Context<T> {
	// __create-react-context-0__ 
	// getUniqueId() starts from 0 by default and +1 each time a new context is created
	const contextProp = '__create-react-context-' + getUniqueId() + '__';

	class Provider extends Component<ProviderProps<T>> {
		// Create a dependency collector and fire events every time this.props. Value changes
		emitter = createEventEmitter(this.props.value);

		// Obsolete API. This property, in conjunction with getChildContext, allows React to automatically pass information down. The custom component accesses the context by defining contextTypes
		static childContextTypes = {
			[contextProp]: PropTypes.object.isRequired
		};

		// Obsolete API, getChildContext is called when the state or props changes.
		// Older versions of React should not use this API, because newer versions of react. CreateContext are not restricted by shouldComponentUpdate, which is obviously affected by the current function shouldComponentUpdate
		getChildContext() {
			return {
				// Return the emitter of the current instance
				[contextProp]: this.emitter
			};
		}

		componentWillReceiveProps(nextProps: any) {
			if (this.props.value ! == nextProps.value) {// Save the reference
				let oldValue = this.props.value;
				let newValue = nextProps.value;
				If 0 is not updated, 0 is not updated
				let changedBits: number;

				if (objectIs(oldValue, newValue)) {
					// No update required
					changedBits = 0; // No change
				} else {
					changedBits =
						typeof calculateChangedBits === 'function'
							? calculateChangedBits(oldValue, newValue)
							: MAX_SIGNED_31_BIT_INT;
					if(process.env.NODE_ENV ! = ='production') {
						warning(
							(changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
							'calculateChangedBits: Expected the return value to be a ' +
							'31-bit integer. Instead received: ' + changedBits,
						);
					}

					// Take an integer to changedBits with | 0
					changedBits |= 0;

					if(changedBits ! = =0) {
						// Trigger the update
						this.emitter.set(nextProps.value, changedBits); }}}}// Returns the current child element
		render() {
			return this.props.children; }}class Consumer extends Component<ConsumerProps<T>, ConsumerState<T>> {
		// Define contextTypes to receive the context context object provided by the Provider
		staticcontextTypes = { [contextProp]: PropTypes.object }; observedBits! : number;// State contains a value attribute
		state: ConsumerState<T> = {
			// The default is the this.context[contextProp] object provided by the Provider
			value: this.getValue()
		};

		componentWillReceiveProps(nextProps: any) {
			// observedBits controls whether or not the current child component is rendered
			let { observedBits } = nextProps;
			this.observedBits =
				observedBits === undefined || observedBits === null
					? MAX_SIGNED_31_BIT_INT // Subscribe to all changes by default
					: observedBits;
		}

		componentDidMount() {
			if (this.context[contextProp]) {
				// Collect dependencies
				this.context[contextProp].on(this.onUpdate);
			}
			let { observedBits } = this.props;
			this.observedBits =
				observedBits === undefined || observedBits === null
					? MAX_SIGNED_31_BIT_INT // Subscribe to all changes by default
					: observedBits;
		}

		componentWillUnmount() {
			// Unlog the listener while uninstalling
			if (this.context[contextProp]) {
				this.context[contextProp].off(this.onUpdate);
			}
		}

		
		getValue(): T {
			// get the contextProp object provided by the Provider. If contextProp exists, call the get method to get value. Otherwise, return the defaultValue defaultValue
			if (this.context[contextProp]) {
				return this.context[contextProp].get();
			} else {
				return defaultValue;
			}
		}

		onUpdate = (newValue: any, changedBits: number) = > {
			/ / integer
			const observedBits: number = this.observedBits | 0;
			if((observedBits & changedBits) ! = =0) {
				// Trigger rendering with this.setstate
				this.setState({ value: this.getValue() }); }};render() {
			RenderFn takes the first child by default and passes in the current value object
			return onlyChild(this.props.children)(this.state.value); }}return {
		Provider,
		Consumer
	};
}



Copy the code

reference

Use globalThis to access global objects

The following references are irrelevant to this article

React source code 解 析 section 6 – expirationTime formula

React Time Slicing-ExpirationTime