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