官网描述该第三方包是React.createContext的ponyfill(用于兼容低版本的react)。与create-react-context体积对比:
| original | mini | |
|---|---|---|
| install size | 50 kB | 140 kB |
| minified | 3.3 kB | 2.3kB |
| minzip | 1.3 kB | 1.0kB |
源码分析
src/index.ts
import React from 'react';
import createReactContext from './implementation';
// 默认导出React.createContext,当React.createContext不存在的时候(低版本的react),导出createReactContext
export default React.createContext || createReactContext;
src/index.d.ts
类型文件
import * as React from 'react';
// 第一个参数为默认值,calculateChangedBits用来控制订阅组件的渲染
export default function createReactContext<T>(
defaultValue: T,
calculateChangedBits?: (prev: T, next: T) => number
): Context<T>;
// Consumer接受一个函数作为子元素,并且返回一个ReactNode
type RenderFn<T> = (value: T) => React.ReactNode;
// Context 包含一个Provider用于发布和Consumer用于订阅
export type Context<T> = {
Provider: React.ComponentClass<ProviderProps<T>>;
Consumer: React.ComponentClass<ConsumerProps<T>>;
};
// Provider props 接受一个value 和 children
export type ProviderProps<T> = {
value: T;
children?: React.ReactNode;
observedBits?: any,
};
// Consumer props 接受一个RenderFn类型的children,observedBits用于控制Consumer子组件的渲染
export type ConsumerProps<T> = {
children: RenderFn<T> | [RenderFn<T>];
observedBits?: number;
};
src/implementation.ts
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import warning from 'tiny-warning';
// 转换为2进制=111111111111111111111111111111
const MAX_SIGNED_31_BIT_INT = 1073741823;
// globalThis的polyfill,用于访问当前的全局上下问环境
const commonjsGlobal: any =
// globalThis 标准属性,用于访问当前环境的全局对象
typeof globalThis !== 'undefined' // 'global proper'
? globalThis
// 浏览器环境,返回window对象
: typeof window !== 'undefined'
? window // Browser
// nodejs 环境,返回global
: typeof global !== 'undefined'
? global // node.js
: {}
// 从全局上下文获取名为__global_unique_id__的值
function getUniqueId() {
const key = '__global_unique_id__';
return commonjsGlobal[key] = (commonjsGlobal[key] || 0) + 1;
}
// Inlined Object.is polyfill.
// Object.is的polyfill,React.createContext的更新比较是通过Object.is这个api来实现的
// 与全等 === 的主要区别在于,+0 -0不相等,NaN 和 NaN 相等。== 进行不同类型的比较时,会先转化为number类型
// 这段代码来自mdn,https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
function objectIs(x: any, y: any) {
if (x === y) {
// 如果x和y都不为0时,返回true
// 如果x和y都为0并且是相同符号,返回true
// 1/-0 -Infinity 1/+0 Infinity
// 用于 +0 -0校验
return x !== 0 || 1 / x === 1 / y;
} else {
// 如果x,y都为NaN,返回true。用于 NaN 和 NaN校验
return x !== x && y !== y;
}
}
// 声明ConsumerState类型,包含一个value属性
export type ConsumerState<T> = {
value: T
};
// 声明RenderFn类型,接受一个value属性并返回一个ReactNode
type RenderFn<T> = (value: T) => React.ReactNode;
// 声明Context,包含Provider和Consumer两个ReactComponent类型的属性
export type Context<T> = {
Provider: React.ComponentClass<ProviderProps<T>>;
Consumer: React.ComponentClass<ConsumerProps<T>>;
};
// 声明Provider接收的props的类型
export type ProviderProps<T> = {
value: T;
children?: React.ReactNode;
observedBits?: any,
};
// 声明Consumer接收的props的类型
export type ConsumerProps<T> = {
children: RenderFn<T> | [RenderFn<T>];
observedBits?: number;
};
// 创建一个EventEmitter函数
function createEventEmitter(value: any) {
let handlers: any[] = [];
return {
// 收集事件
on(handler: any) {
handlers.push(handler);
},
// 注销事件
off(handler: any) {
handlers = handlers.filter(h => h !== handler);
},
// 获取value值
get() {
return value;
},
// 设置value值时同时触发当前注册的监听函数
set(newValue: any, changedBits: any) {
value = newValue;
handlers.forEach(handler => handler(value, changedBits));
}
};
}
// 判断children是否是一个数组,如果是,只返回第一个子元素。用在Consumer中
function onlyChild(children: any): any {
return Array.isArray(children) ? children[0] : children;
}
// calculateChangedBits 用来控制是否需要渲染,返回值为0的时候,不渲染订阅的子组件
export default function createReactContext<T>(defaultValue: T, calculateChangedBits?: (a: T, b: T) => number): Context<T> {
// __create-react-context-0__
// getUniqueId() 默认从0开始,每次新建context的时候+1
const contextProp = '__create-react-context-' + getUniqueId() + '__';
class Provider extends Component<ProviderProps<T>> {
// 创建一个依赖收集器,并在每次this.props.value变动的时候,触发事件
emitter = createEventEmitter(this.props.value);
// 过时api,该属性配合getChildContext可以实现React 自动向下传递信息,自组件通过定义 contextTypes 来访问 context
static childContextTypes = {
[contextProp]: PropTypes.object.isRequired
};
// 过时api,当 state 或者 props 改变的时候,getChildContext会被调用。
// 高版本的react不应该使用此api,因为高版本React.createContext的更新并不受shouldComponentUpdate的限制,而当前函数很明显会受到shouldComponentUpdate的影响
getChildContext() {
return {
// 返回当前实例的emitter
[contextProp]: this.emitter
};
}
componentWillReceiveProps(nextProps: any) {
if (this.props.value !== nextProps.value) {
// 保存引用
let oldValue = this.props.value;
let newValue = nextProps.value;
// 判断是否需要更新,0不更新,不为0更新
let changedBits: number;
if (objectIs(oldValue, newValue)) {
// 不需要更新
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,
);
}
// 按位或运算 |0 给changedBits取整数
changedBits |= 0;
if (changedBits !== 0) {
// 触发更新
this.emitter.set(nextProps.value, changedBits);
}
}
}
}
// 返回当前的子元素
render() {
return this.props.children;
}
}
class Consumer extends Component<ConsumerProps<T>, ConsumerState<T>> {
// 定义contextTypes用于接收Provider提供的context上下文对象
static contextTypes = {
[contextProp]: PropTypes.object
};
observedBits!: number;
// state 包含一个value属性
state: ConsumerState<T> = {
// 默认值为Provider提供的this.context[contextProp]对象
value: this.getValue()
};
componentWillReceiveProps(nextProps: any) {
// 通过observedBits可以控制当前子组件是否渲染
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]) {
// 收集依赖
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() {
// 卸载时同时注销监听函数
if (this.context[contextProp]) {
this.context[contextProp].off(this.onUpdate);
}
}
getValue(): T {
// 获取Provider提供的contextProp对象, contextProp存在,则调用get方法获取value,否则返回默认值defaultValue
if (this.context[contextProp]) {
return this.context[contextProp].get();
} else {
return defaultValue;
}
}
onUpdate = (newValue: any, changedBits: number) => {
// 取整
const observedBits: number = this.observedBits | 0;
if ((observedBits & changedBits) !== 0) {
// 通过this.setState触发渲染
this.setState({ value: this.getValue() });
}
};
render() {
// RenderFn默认取第一个子元素并传入当前的value对象
return onlyChild(this.props.children)(this.state.value);
}
}
return {
Provider,
Consumer
};
}
参考
以下参考与本文内容无关