Commit 3fe0ceb4 authored by Shen Chang's avatar Shen Chang

refactor(src): Rewrite with typescript, Added include and exclude

parent a089fc4a
......@@ -892,6 +892,20 @@
"to-fast-properties": "^2.0.0"
}
},
"@types/hoist-non-react-statics": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz",
"integrity": "sha512-3wTz66vV+WatOAjMST+hKCmo01KYPFgnsu+QeLcn0FuwPCoymX6aj1a4RvFCdVsfh2m0hfTPhE/zTv4M28ho1Q==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/js-md5": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@types/js-md5/-/js-md5-0.4.2.tgz",
"integrity": "sha512-FUPoQkpQTzA5wz9ebrdVRjsjQsFehr+cW1CVhLcI2UwD/SO/4NHPO1esrXPPbx7ux762U0POmWFSrUjQq2ophw=="
},
"@types/node": {
"version": "10.12.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.29.tgz",
......@@ -914,6 +928,15 @@
"csstype": "^2.2.0"
}
},
"@types/react-dom": {
"version": "16.8.2",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.8.2.tgz",
"integrity": "sha512-MX7n1wq3G/De15RGAAqnmidzhr2Y9O/ClxPxyqaNg96pGyeXUYPSvujgzEVpLo9oIP4Wn1UETl+rxTN02KEpBw==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
......
......@@ -2,42 +2,50 @@ import React from 'react';
import ReactDOM from 'react-dom';
import noop from '../utils/noop';
export default class Comment extends React.PureComponent {
parentNode = null;
currentNode = null;
commentNode = null;
content = null;
componentDidMount() {
const node = ReactDOM.findDOMNode(this);
interface IReactCommentProps {
onLoaded: () => void;
}
class ReactComment extends React.PureComponent<IReactCommentProps> {
public static defaultProps = {
onLoaded: noop,
};
private parentNode: Node;
private currentNode: Element;
private commentNode: Comment;
private content: string;
public componentDidMount() {
const node = ReactDOM.findDOMNode(this) as Element;
const commentNode = this.createComment();
this.commentNode = commentNode;
this.currentNode = node;
this.parentNode = node.parentNode;
this.parentNode = node.parentNode as Node;
this.parentNode.replaceChild(commentNode, node);
ReactDOM.unmountComponentAtNode(node);
this.props.onLoaded();
}
componentWillUnmount() {
public componentWillUnmount() {
this.parentNode.replaceChild(this.currentNode, this.commentNode);
}
createComment() {
private createComment() {
let content = this.props.children;
if (typeof content !== 'string') {
content = '';
}
content = content.trim();
this.content = content;
return document.createComment(content);
this.content = (content as string).trim();
return document.createComment(this.content);
}
render() {
public render() {
return <div />;
}
}
Comment.defaultProps = {
onLoaded: noop,
};
\ No newline at end of file
export default ReactComment;
import React from 'react';
import Comment from './Comment';
import {LIFECYCLE, ICache, ICacheItem} from './Provider';
import findDOMNodeByFiberNode from '../utils/findDOMNodeByFiberNode';
import createUniqueIdentification from '../utils/createUniqueIdentification';
export const LIFECYCLE = {
MOUNTED: 0,
UPDATING: 1,
UNMOUNTED: 2,
};
interface IConsumerProps {
children: React.ReactNode;
identification: string;
keepAlive: boolean;
cache: ICache;
setCache: (identification: string, value: ICacheItem) => void;
unactivate: (identification: string) => void;
}
class Consumer extends React.PureComponent {
renderElement = null;
class Consumer extends React.PureComponent<IConsumerProps> {
private renderElement: HTMLElement;
identification = this.props.identification;
private identification: string = this.props.identification;
// This attribute is designed to prevent duplicates of the identification of KeepAlive components.
key = createUniqueIdentification();
private key: string = createUniqueIdentification();
constructor(props) {
super(props);
constructor(props: IConsumerProps, ...args: any) {
super(props, ...args);
const {cache, setCache, children} = props;
if (!cache || !setCache) {
throw new Error('<KeepAlive> component must be in the <Provider> component.');
......@@ -26,14 +30,14 @@ class Consumer extends React.PureComponent {
React.Children.only(children);
}
componentDidMount() {
public componentDidMount() {
const {
setCache,
children,
keepAlive,
} = this.props;
const {_reactInternalFiber} = this;
this.renderElement = findDOMNodeByFiberNode(_reactInternalFiber);
const {_reactInternalFiber} = this as any;
this.renderElement = findDOMNodeByFiberNode(_reactInternalFiber) as HTMLElement;
setCache(this.identification, {
children,
keepAlive,
......@@ -44,7 +48,7 @@ class Consumer extends React.PureComponent {
});
}
componentDidUpdate() {
public componentDidUpdate() {
const {
setCache,
children,
......@@ -57,12 +61,12 @@ class Consumer extends React.PureComponent {
});
}
componentWillUnmount() {
public componentWillUnmount() {
const {unactivate} = this.props;
unactivate(this.identification);
}
render() {
public render() {
const {identification} = this;
return <Comment>{identification}</Comment>;
}
......
import React from 'react';
import ReactDOM from 'react-dom';
import Comment from './Comment';
import {LIFECYCLE} from './Consumer';
import KeepAliveContext from '../contexts/KeepAliveContext';
import createEventEmitter from '../utils/createEventEmitter';
import createUniqueIdentification from '../utils/createUniqueIdentification';
......@@ -10,79 +9,119 @@ import createStoreElement from '../utils/createStoreElement';
export const keepAliveProviderTypeName = 'KeepAliveProvider';
export const START_MOUNTING_DOM = 'startMountingDOM';
export enum LIFECYCLE {
MOUNTED,
UPDATING,
UNMOUNTED,
}
export interface ICacheItem {
children: React.ReactNode;
keepAlive: boolean;
lifecycle: LIFECYCLE;
key?: string | null;
renderElement?: HTMLElement;
activated?: boolean;
ifStillActivate?: boolean;
reactivate?: () => void;
}
export interface ICache {
[key: string]: ICacheItem;
}
export interface IKeepAliveProviderImpl {
storeElement: HTMLElement;
cache: ICache;
keys: string[];
eventEmitter: any;
existed: boolean;
providerIdentification: string;
setCache: (identification: string, value: ICacheItem) => void;
unactivate: (identification: string) => void;
isExisted: () => boolean;
}
export interface IKeepAliveProviderProps {
include?: string | string[] | RegExp;
exclude?: string | string[] | RegExp;
}
// TODO: include max exclude
export default class KeepAliveProvider extends React.PureComponent {
storeElement = createStoreElement();
export default class KeepAliveProvider extends React.PureComponent<IKeepAliveProviderProps> implements IKeepAliveProviderImpl {
public static displayName = keepAliveProviderTypeName;
public storeElement = createStoreElement();
// Sometimes data that changes with setState cannot be synchronized, so force refresh
cache = Object.create(null);
public cache: ICache = Object.create(null);
keys = [];
public keys: string[] = [];
eventEmitter = createEventEmitter();
public eventEmitter = createEventEmitter();
existed = true;
public existed: boolean = true;
needRerender = false;
private needRerender: boolean = false;
providerIdentification = createUniqueIdentification();
public providerIdentification: string = createUniqueIdentification();
componentDidUpdate() {
public componentDidUpdate() {
if (this.needRerender) {
this.needRerender = false;
this.forceUpdate();
}
}
componentWillUnmount() {
public componentWillUnmount() {
this.eventEmitter.clear();
this.existed = false;
document.body.removeChild(this.storeElement);
}
isExisted = () => {
public isExisted = () => {
return this.existed;
};
}
setCache = (identification, value) => {
public setCache = (identification: string, value: ICacheItem) => {
const {cache, keys} = this;
const {key} = cache[identification] || {};
if (!key) {
keys.push(identification);
// this.shiftKey();
}
if (key && value.key && key !== value.key) {
const currentCache = cache[identification];
const key = currentCache && currentCache.key;
if (key && value.key && key !== (value.key as unknown)) {
throw new Error('Cached components have duplicates.');
}
if (!currentCache) {
keys.push(identification);
}
this.cache[identification] = {
...cache[identification],
...currentCache,
...value,
};
this.forceUpdate();
};
getMax = () => {
return this.props.max ? parseInt(this.props.max) : null;
};
shiftKey = () => {
const max = this.getMax();
const {keys, cache} = this;
if (!max || keys.length <= max) {
return;
}
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const currentCache = cache[key];
if (currentCache && !currentCache.activated) {
keys.splice(i, 1);
delete cache[key];
return;
}
}
}
unactivate = identification => {
// private getMax = () => {
// return this.props.max ? parseInt(this.props.max) : null;
// }
// private shiftKey = () => {
// const max = this.getMax();
// const {keys, cache} = this;
// if (!max || keys.length <= max) {
// return;
// }
// for (let i = 0; i < keys.length; i++) {
// const key = keys[i];
// const currentCache = cache[key];
// if (currentCache && !currentCache.activated) {
// keys.splice(i, 1);
// delete cache[key];
// return;
// }
// }
// }
public unactivate = (identification: string) => {
const {cache} = this;
this.cache[identification] = {
...cache[identification],
......@@ -91,25 +130,25 @@ export default class KeepAliveProvider extends React.PureComponent {
lifecycle: LIFECYCLE.UNMOUNTED,
};
this.forceUpdate();
};
}
startMountingDOM = identification => {
private startMountingDOM = (identification: string) => {
this.eventEmitter.emit([identification, START_MOUNTING_DOM]);
};
}
render() {
public render() {
const {
cache,
keys,
providerIdentification,
isExisted,
setCache,
unactivate,
shiftKey,
storeElement,
eventEmitter,
} = this;
const {
children,
children: innerChildren,
include,
exclude,
} = this.props;
......@@ -120,7 +159,6 @@ export default class KeepAliveProvider extends React.PureComponent {
providerIdentification,
isExisted,
setCache,
shiftKey,
unactivate,
storeElement,
eventEmitter,
......@@ -129,19 +167,15 @@ export default class KeepAliveProvider extends React.PureComponent {
}}
>
<React.Fragment>
{children}
{
Object.entries(cache).map((
[
identification,
{innerChildren}
{
keys.map(identification => {
const currentCache = cache[identification];
const {
keepAlive,
children,
lifecycle,
},
],
) => {
const currentCache = cache[identification];
} = currentCache;
let cacheChildren = children;
if (lifecycle === LIFECYCLE.MOUNTED && !keepAlive) {
// If the cache was last enabled, then the components of this keepAlive package are used,
......
import React from 'react';
const WithKeepAliveContext = React.createContext();
export default WithKeepAliveContext;
\ No newline at end of file
import React from 'react';
const WithKeepAliveContext = React.createContext({});
export default WithKeepAliveContext;
import React from 'react';
const KeepAliveContext = React.createContext();
export default KeepAliveContext;
\ No newline at end of file
import React from 'react';
const KeepAliveContext = React.createContext({});
export default KeepAliveContext;
import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import IdentificationContext from '../contexts/IdentificationContext';
import Consumer, {LIFECYCLE} from '../components/Consumer';
import {START_MOUNTING_DOM} from '../components/Provider';
import md5 from './md5';
import noop from './noop';
import getContextIdentificationByFiberNode from './getContextIdentificationByFiberNode';
import withIdentificationConsumer from './withIdentificationContextConsumer';
import withKeepAliveConsumer from './withKeepAliveContextConsumer';
import changePositionByComment from './changePositionByComment';
import getDisplayName from './getDisplayName';
import getKeepAlive from './getKeepAlive';
export const COMMAND = {
UNACTIVATE: 'unactivate',
UNMOUNT: 'unmount',
ACTIVATE: 'activate',
};
export default function keepAlive({
name,
forwardRef = false,
} = {}) {
return Component => {
const {
componentDidMount = noop,
componentDidUpdate = noop,
componentDidActivate = noop,
componentWillUnactivate = noop,
componentWillUnmount = noop,
} = Component.prototype;
const displayName = name || getDisplayName(Component);
if (!displayName) {
throw new Error('Each component must have a name, which can be the component\'s displayName or name static property. You can also configure name when keepAlive decorates the component.');
}
Component.prototype.componentDidMount = function () {
const {
_container,
keepAlive,
} = this.props;
const {
notNeedActivate,
identification,
eventEmitter,
} = _container;
notNeedActivate();
const cb = () => {
mount.call(this);
eventEmitter.off([identification, START_MOUNTING_DOM], cb);
};
eventEmitter.on([identification, START_MOUNTING_DOM], cb);
componentDidMount.call(this);
if (keepAlive) {
componentDidActivate.call(this);
}
};
Component.prototype.componentDidUpdate = function () {
componentDidUpdate.call(this);
const {
_container,
} = this.props;
const {
notNeedActivate,
isNeedActivate,
} = _container;
if (isNeedActivate()) {
notNeedActivate();
mount.call(this);
this._unmounted = false;
componentDidActivate.call(this);
}
};
Component.prototype.componentWillUnactivate = function () {
componentWillUnactivate.call(this);
unmount.call(this);
};
Component.prototype.componentWillUnmount = function () {
// Because we will manually call the componentWillUnmount lifecycle
// so we need to prevent it from firing multiple times
if (!this._unmounted) {
this._unmounted = true;
componentWillUnmount.call(this);
unmount.call(this);
}
};
function mount() {
const {
_container: {
cache,
identification,
storeElement,
setLifecycle,
},
} = this.props;
const {renderElement} = cache[identification];
setLifecycle(LIFECYCLE.UPDATING);
changePositionByComment(identification, renderElement, storeElement);
}
function unmount() {
const {
_container: {
identification,
storeElement,
cache,
setLifecycle,
},
} = this.props;
const {renderElement, ifStillActivate, reactivate} = cache[identification];
setLifecycle(LIFECYCLE.UNMOUNTED);
changePositionByComment(identification, storeElement, renderElement);
if (ifStillActivate) {
reactivate();
}
}
@withKeepAliveConsumer
class TriggerLifecycleContainer extends React.PureComponent {
identification = null;
ref = null;
activated = false;
ifStillActivate = false;
// Let the lifecycle of the cached component be called normally.
needActivate = true;
lifecycle = LIFECYCLE.MOUNTED;
componentDidMount() {
if (!this.ifStillActivate) {
this.activate();
}
const {eventEmitter, keepAlive} = this.props;
if (keepAlive) {
this.needActivate = true;
eventEmitter.emit([this.identification, COMMAND.ACTIVATE]);
}
}
componentDidCatch() {
if (!this.activated) {
this.activate();
}
}
componentWillUnmount() {
const {eventEmitter, isExisted, getCombinedKeepAlive} = this.props;
const keepAlive = getCombinedKeepAlive();
if (!keepAlive || !isExisted()) {
if (this.ref) {
this.ref.componentWillUnmount();
}
eventEmitter.emit([this.identification, COMMAND.UNMOUNT]);
}
// When the Provider components are unmounted, the cache is not needed,
// so you don't have to execute the componentWillUnactivate lifecycle.
if (keepAlive && isExisted()) {
if (this.ref) {
this.ref.componentWillUnactivate();
}
eventEmitter.emit([this.identification, COMMAND.UNACTIVATE]);
}
}
activate = () => {
this.activated = true;
};
reactivate = () => {
this.ifStillActivate = false;
this.forceUpdate();
};
isNeedActivate = () => {
return this.needActivate;
};
notNeedActivate = () => {
this.needActivate = false;
};
getLifecycle = () => {
return this.lifecycle;
};
setLifecycle = lifecycle => {
this.lifecycle = lifecycle;
};
setRef = ref => {
this.ref = ref;
const {
forwardedRef,
} = this.props;
if (forwardedRef) {
forwardedRef(ref);
}
};
render() {
if (!this.identification) {
// We need to generate a corresponding unique identifier based on the information of the component.
const {providerIdentification, cache} = this.props;
const {
paths,
globalKey,
typeNames,
} = getContextIdentificationByFiberNode(this._reactInternalFiber);
this.identification = md5(
`${providerIdentification}${displayName}${globalKey ? `${globalKey}${typeNames}` : paths}`,
);
// The last activated component must be unactivated before it can be activated again.
const currentCache = cache[this.identification];
if (currentCache) {
this.ifStillActivate = currentCache.activated;
currentCache.ifStillActivate = this.ifStillActivate;
currentCache.reactivate = this.reactivate;
}
}
const {
isNeedActivate,
notNeedActivate,
activated,
getLifecycle,
setLifecycle,
setRef,
identification,
ifStillActivate,
} = this;
const {
eventEmitter,
keepAlive,
unactivate,
setCache,
forwardedRef,
isExisted,
storeElement,
cache,
providerIdentification,
...wrapperProps
} = this.props;
return !ifStillActivate
? (
<Consumer
identification={identification}
keepAlive={keepAlive}
>
<IdentificationContext.Provider
value={{
_identification: identification,
_eventEmitter: eventEmitter,
_keepAlive: keepAlive,
_activated: activated,
_getLifecycle: getLifecycle,
_isExisted: isExisted,
}}
>
<Component
{...wrapperProps}
keepAlive={keepAlive}
ref={setRef}
_container={{
isNeedActivate,
notNeedActivate,
setLifecycle,
eventEmitter,
identification,
storeElement,
cache,
}}
/>
</IdentificationContext.Provider>
</Consumer>
)
: null;
}
}
@withIdentificationConsumer
class ListenUpperKeepAliveContainer extends React.PureComponent {
combinedKeepAlive = this.props.keepAlive;
state = {
activated: true,
};
componentDidMount() {
this.listenUpperKeepAlive();
}
componentWillUnmount() {
this.unlistenUpperKeepAlive();
}
listenUpperKeepAlive() {
const {_identification, _eventEmitter} = this.props;
if (!_identification) {
return;
}
_eventEmitter.on(
[_identification, COMMAND.ACTIVATE],
this.activate = () => this.setState({activated: true}),
true,
);
_eventEmitter.on(
[_identification, COMMAND.UNACTIVATE],
this.unactivate = () => this.setState({activated: false}),
true,
);
_eventEmitter.on(
[_identification, COMMAND.UNMOUNT],
this.unmount = () => this.setState({activated: false}),
true,
);
}
unlistenUpperKeepAlive() {
const {_identification, _eventEmitter} = this.props;
if (!_identification) {
return;
}
_eventEmitter.off([_identification, COMMAND.ACTIVATE], this.activate);
_eventEmitter.off([_identification, COMMAND.UNACTIVATE], this.unactivate);
_eventEmitter.off([_identification, COMMAND.UNMOUNT], this.unmount);
}
getCombinedKeepAlive = () => {
return this.combinedKeepAlive;
};
render() {
const {
_identification,
_eventEmitter,
_keepAlive,
_activated,
_getLifecycle,
keepAlive,
...wrapperProps
} = this.props;
const {activated} = this.state;
// When the parent KeepAlive component is mounted or unmounted,
// use the keepAlive prop of the parent KeepAlive component.
const newKeepAlive = getKeepAlive(keepAlive);
this.combinedKeepAlive = _getLifecycle === undefined || _getLifecycle() === LIFECYCLE.UPDATING
? newKeepAlive
: _identification
? _keepAlive && newKeepAlive
: newKeepAlive;
return activated
? (
<TriggerLifecycleContainer
{...wrapperProps}
keepAlive={this.combinedKeepAlive}
getCombinedKeepAlive={this.getCombinedKeepAlive}
/>
)
: null;
}
}
let NewComponent = ListenUpperKeepAliveContainer;
if (forwardRef) {
NewComponent = React.forwardRef((props, ref) => (
<ListenUpperKeepAliveContainer
{...props}
forwardedRef={ref}
/>
));
}
NewComponent.displayName = `keepAlive(${displayName})`;
return hoistNonReactStatics(NewComponent, Component);
};
}
\ No newline at end of file
......@@ -5,7 +5,7 @@ import {COMMAND} from './keepAlive';
import withIdentificationContextConsumer from './withIdentificationContextConsumer';
import getDisplayName from './getDisplayName';
export default function bindLifecycle(Component) {
export default function bindLifecycle<P = any>(Component: React.ComponentType<P>) {
const {
componentDidMount = noop,
componentDidUpdate = noop,
......@@ -117,4 +117,4 @@ export default function bindLifecycle(Component) {
)),
Component,
);
};
}
const NODE_TYPES = {
ELEMENT: 1,
COMMENT: 8,
};
enum NODE_TYPES {
ELEMENT = 1,
COMMENT = 8,
}
function findElementsBetweenComments(node, identification) {
function findElementsBetweenComments(node: Node, identification: string): Node[] {
const elements = [];
const childNodes = node.childNodes;
const childNodes = node.childNodes as any;
let startCommentExist = false;
for (let i = 0; i < childNodes.length; i++) {
const child = childNodes[i];
for (const child of childNodes) {
if (
child.nodeType === NODE_TYPES.COMMENT &&
child.nodeValue.trim() === identification &&
......@@ -24,10 +23,9 @@ function findElementsBetweenComments(node, identification) {
return elements;
}
function findComment(node, identification) {
const childNodes = node.childNodes;
for (let i = 0; i < childNodes.length; i++) {
const child = childNodes[i];
function findComment(node: Node, identification: string): Node | undefined {
const childNodes = node.childNodes as any;
for (const child of childNodes) {
if (
child.nodeType === NODE_TYPES.COMMENT &&
child.nodeValue.trim() === identification
......@@ -37,7 +35,7 @@ function findComment(node, identification) {
}
}
export default function changePositionByComment(identification, presentParentNode, originalParentNode) {
export default function changePositionByComment(identification: string, presentParentNode: Node, originalParentNode: Node) {
if (!presentParentNode || !originalParentNode) {
return;
}
......@@ -46,8 +44,8 @@ export default function changePositionByComment(identification, presentParentNod
if (!elementNodes.length || !commentNode) {
return;
}
elementNodes.push(elementNodes[elementNodes.length - 1].nextSibling);
elementNodes.unshift(elementNodes[0].previousSibling);
elementNodes.push(elementNodes[elementNodes.length - 1].nextSibling as Node);
elementNodes.unshift(elementNodes[0].previousSibling as Node);
// Deleting comment elements when using commet components will result in component uninstallation errors
for (let i = elementNodes.length - 1; i >= 0; i--) {
presentParentNode.insertBefore(elementNodes[i], commentNode);
......
type EventNames = string | string[];
type Listener = (...args: any) => void;
export default function createEventEmitter() {
let events = Object.create(null);
function on(eventNames, listener, direction = false) {
function on(eventNames: EventNames, listener: Listener, direction = false) {
eventNames = getEventNames(eventNames);
let current = events;
const maxIndex = eventNames.length - 1;
......@@ -9,7 +13,7 @@ export default function createEventEmitter() {
const key = eventNames[i];
if (!current[key]) {
current[key] = i === maxIndex ? [] : {};
};
}
current = current[key];
}
if (!Array.isArray(current)) {
......@@ -22,41 +26,43 @@ export default function createEventEmitter() {
}
}
function off(eventNames, listener) {
function off(eventNames: EventNames, listener: Listener) {
const listeners = getListeners(eventNames);
if (!listeners) {
return;
}
const matchIndex = listeners.findIndex(v => v === listener);
const matchIndex = listeners.findIndex((v: Listener) => v === listener);
if (matchIndex !== -1) {
listeners.splice(matchIndex, 1);
}
}
function removeAllListeners(eventNames) {
function removeAllListeners(eventNames: EventNames) {
const listeners = getListeners(eventNames);
if (!listeners) {
return;
}
eventNames = getEventNames(eventNames);
const lastEventName = eventNames.pop();
if (lastEventName) {
const event = eventNames.reduce((obj, key) => obj[key], events);
event[lastEventName] = [];
}
}
function emit(eventNames, ...args) {
function emit(eventNames: EventNames, ...args: any) {
const listeners = getListeners(eventNames);
if (!listeners) {
return;
}
for (let i = 0; i < listeners.length; i++) {
if (listeners[i]) {
listeners[i](...args);
for (const listener of listeners) {
if (listener) {
listener(...args);
}
}
}
function listenerCount(eventNames) {
function listenerCount(eventNames: EventNames) {
const listeners = getListeners(eventNames);
return listeners ? listeners.length : 0;
}
......@@ -65,14 +71,16 @@ export default function createEventEmitter() {
events = Object.create(null);
}
function getListeners(eventNames) {
function getListeners(eventNames: EventNames): Listener[] | undefined {
eventNames = getEventNames(eventNames);
try {
return eventNames.reduce((obj, key) => obj[key], events);
} catch (e) {}
} catch (e) {
return;
}
}
function getEventNames(eventNames) {
function getEventNames(eventNames: EventNames): string[] {
if (!eventNames) {
throw new Error('Must exist event name.');
}
......@@ -90,4 +98,4 @@ export default function createEventEmitter() {
listenerCount,
removeAllListeners,
};
};
\ No newline at end of file
}
import {prefix} from './createUniqueIdentification';
export default function createStoreElement() {
export default function createStoreElement(): HTMLElement {
const keepAliveDOM = document.createElement('div');
keepAliveDOM.dataset.type = prefix;
keepAliveDOM.style.display = 'none';
......
......@@ -10,7 +10,7 @@ export const prefix = 'keep-alive';
*/
export default function createUniqueIdentification(length = 6) {
const strings = [];
for (var i = 0; i < length; i++) {
for (let i = 0; i < length; i++) {
strings[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
return `${prefix}-${strings.join('')}`;
......
export default function findDOMNodeByFiberNode(fiberNode) {
export default function findDOMNodeByFiberNode(fiberNode: any): HTMLElement | null {
if (!fiberNode) {
return null;
}
......
import React from 'react';
import {keepAliveProviderTypeName} from '../components/Provider';
import {keepAliveDisplayName} from './keepAlive';
export default function getContextIdentificationByFiberNode(fiberNode) {
let globalKey = null;
export default function getContextIdentificationByFiberNode(fiberNode: any) {
let globalKey: React.Key | null = null;
let typeNames = '';
function getPathsByFiberNode(fiberNode) {
function getPathsByFiberNode(fiberNode: any) {
if (!fiberNode) {
return '';
}
......@@ -12,12 +14,12 @@ export default function getContextIdentificationByFiberNode(fiberNode) {
key,
index,
} = fiberNode;
let typeName = type && type.name ? type.name : '';
const typeName = type && type.displayName;
if (typeName === keepAliveProviderTypeName) {
return '';
}
const joinName = getPathsByFiberNode(fiberNode.return);
if (type && type.displayName && type.displayName.indexOf('keepAlive') !== -1) {
const joinName: string = getPathsByFiberNode(fiberNode.return);
if (type && type.displayName && type.displayName.indexOf(keepAliveDisplayName) !== -1) {
if (!globalKey) {
globalKey = key;
}
......
export default function getDisplayName(Component) {
import React from 'react';
export default function getDisplayName(Component: React.ComponentType) {
return Component.displayName || Component.name || null;
}
export default function getKeepAlive(keepAlive) {
return keepAlive === undefined ? true : keepAlive;
}
\ No newline at end of file
import isRegExp from './isRegExp';
type Pattern = string | string[] | RegExp;
function matches (pattern: Pattern, name: string) {
if (Array.isArray(pattern)) {
return pattern.indexOf(name) > -1;
} else if (typeof pattern === 'string') {
return pattern.split(',').indexOf(name) > -1;
} else if (isRegExp(pattern)) {
return pattern.test(name);
}
return false;
}
export default function getKeepAlive(
name: string,
include?: Pattern,
exclude?: Pattern,
keepAlive?: boolean
) {
if (keepAlive !== undefined) {
return keepAlive;
}
if (
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return false;
}
return true;
}
export default function isRegExp(value: RegExp) {
return value && Object.prototype.toString.call(value) === '[object RegExp]';
}
import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import IdentificationContext from '../contexts/IdentificationContext';
import Consumer, {LIFECYCLE} from '../components/Consumer';
import {START_MOUNTING_DOM} from '../components/Provider';
import md5 from './md5';
import noop from './noop';
import getContextIdentificationByFiberNode from './getContextIdentificationByFiberNode';
import withIdentificationConsumer from './withIdentificationConsumer';
import withKeepAliveConsumer from './withKeepAliveConsumer';
import changePositionByComment from './changePositionByComment';
import getDisplayName from './getDisplayName';
import getKeepAlive from './getKeepAlive';
export const COMMAND = {
UNACTIVATE: 'unactivate',
UNMOUNT: 'unmount',
ACTIVATE: 'activate',
};
export default function keepAlive({
name,
forwardRef = false,
} = {}) {
return Component => {
const {
componentDidMount = noop,
componentDidUpdate = noop,
componentDidActivate = noop,
componentWillUnactivate = noop,
componentWillUnmount = noop,
} = Component.prototype;
const displayName = name || getDisplayName(Component);
if (!displayName) {
throw new Error('Each component must have a name, which can be the component\'s displayName or name static property. You can also configure name when keepAlive decorates the component.');
}
Component.prototype.componentDidMount = function () {
const {
_container,
keepAlive,
} = this.props;
const {
notNeedActivate,
identification,
eventEmitter,
} = _container;
notNeedActivate();
const cb = () => {
mount.call(this);
eventEmitter.off([identification, START_MOUNTING_DOM], cb);
};
eventEmitter.on([identification, START_MOUNTING_DOM], cb);
componentDidMount.call(this);
if (keepAlive) {
componentDidActivate.call(this);
}
};
Component.prototype.componentDidUpdate = function () {
componentDidUpdate.call(this);
const {
_container,
} = this.props;
const {
notNeedActivate,
isNeedActivate,
} = _container;
if (isNeedActivate()) {
notNeedActivate();
mount.call(this);
this._unmounted = false;
componentDidActivate.call(this);
}
};
Component.prototype.componentWillUnactivate = function () {
componentWillUnactivate.call(this);
unmount.call(this);
};
Component.prototype.componentWillUnmount = function () {
// Because we will manually call the componentWillUnmount lifecycle
// so we need to prevent it from firing multiple times
if (!this._unmounted) {
this._unmounted = true;
componentWillUnmount.call(this);
unmount.call(this);
}
};
function mount() {
const {
_container: {
cache,
identification,
storeElement,
setLifecycle,
},
} = this.props;
const {renderElement} = cache[identification];
setLifecycle(LIFECYCLE.UPDATING);
changePositionByComment(identification, renderElement, storeElement);
}
function unmount() {
const {
_container: {
identification,
storeElement,
cache,
setLifecycle,
},
} = this.props;
const {renderElement, ifStillActivate, reactivate} = cache[identification];
setLifecycle(LIFECYCLE.UNMOUNTED);
changePositionByComment(identification, storeElement, renderElement);
if (ifStillActivate) {
reactivate();
}
}
class TriggerLifecycleContainer extends React.PureComponent {
identification = null;
ref = null;
activated = false;
ifStillActivate = false;
// Let the lifecycle of the cached component be called normally.
needActivate = true;
lifecycle = LIFECYCLE.MOUNTED;
componentDidMount() {
if (!this.ifStillActivate) {
this.activate();
}
const {
keepAlive,
_keepAliveContextProps: {
eventEmitter,
},
} = this.props;
if (keepAlive) {
this.needActivate = true;
eventEmitter.emit([this.identification, COMMAND.ACTIVATE]);
}
}
componentDidCatch() {
if (!this.activated) {
this.activate();
}
}
componentWillUnmount() {
const {
getCombinedKeepAlive,
_keepAliveContextProps: {
eventEmitter,
isExisted,
},
} = this.props;
const keepAlive = getCombinedKeepAlive();
if (!keepAlive || !isExisted()) {
if (this.ref) {
this.ref.componentWillUnmount();
}
eventEmitter.emit([this.identification, COMMAND.UNMOUNT]);
}
// When the Provider components are unmounted, the cache is not needed,
// so you don't have to execute the componentWillUnactivate lifecycle.
if (keepAlive && isExisted()) {
if (this.ref) {
this.ref.componentWillUnactivate();
}
eventEmitter.emit([this.identification, COMMAND.UNACTIVATE]);
}
}
activate = () => {
this.activated = true;
};
reactivate = () => {
this.ifStillActivate = false;
this.forceUpdate();
};
isNeedActivate = () => {
return this.needActivate;
};
notNeedActivate = () => {
this.needActivate = false;
};
getLifecycle = () => {
return this.lifecycle;
};
setLifecycle = lifecycle => {
this.lifecycle = lifecycle;
};
setRef = ref => {
this.ref = ref;
const {
forwardedRef,
} = this.props;
if (forwardedRef) {
forwardedRef(ref);
}
};
render() {
if (!this.identification) {
// We need to generate a corresponding unique identifier based on the information of the component.
const {providerIdentification, cache} = this.props._keepAliveContextProps;
const {
paths,
globalKey,
typeNames,
} = getContextIdentificationByFiberNode(this._reactInternalFiber);
this.identification = md5(
`${providerIdentification}${displayName}${globalKey ? `${globalKey}${typeNames}` : paths}`,
);
// The last activated component must be unactivated before it can be activated again.
const currentCache = cache[this.identification];
if (currentCache) {
this.ifStillActivate = currentCache.activated;
currentCache.ifStillActivate = this.ifStillActivate;
currentCache.reactivate = this.reactivate;
}
}
const {
isNeedActivate,
notNeedActivate,
activated,
getLifecycle,
setLifecycle,
setRef,
identification,
ifStillActivate,
} = this;
const {
keepAlive,
forwardedRef,
_keepAliveContextProps: {
isExisted,
storeElement,
cache,
eventEmitter,
},
...wrapperProps
} = this.props;
return !ifStillActivate
? (
<Consumer
identification={identification}
keepAlive={keepAlive}
>
<IdentificationContext.Provider
value={{
identification,
eventEmitter,
keepAlive,
activated,
getLifecycle,
isExisted,
}}
>
<Component
{...wrapperProps}
keepAlive={keepAlive}
ref={setRef}
_container={{
isNeedActivate,
notNeedActivate,
setLifecycle,
eventEmitter,
identification,
storeElement,
cache,
}}
/>
</IdentificationContext.Provider>
</Consumer>
)
: null;
}
}
@withIdentificationConsumer
@withKeepAliveConsumer
class ListenUpperKeepAliveContainer extends React.PureComponent {
combinedKeepAlive = this.props.keepAlive;
state = {
activated: true,
};
componentDidMount() {
this.listenUpperKeepAlive();
}
componentWillUnmount() {
this.unlistenUpperKeepAlive();
}
listenUpperKeepAlive() {
const {identification, eventEmitter} = this.props._identificationContextProps;
if (!identification) {
return;
}
eventEmitter.on(
[identification, COMMAND.ACTIVATE],
this.activate = () => this.setState({activated: true}),
true,
);
eventEmitter.on(
[identification, COMMAND.UNACTIVATE],
this.unactivate = () => this.setState({activated: false}),
true,
);
eventEmitter.on(
[identification, COMMAND.UNMOUNT],
this.unmount = () => this.setState({activated: false}),
true,
);
}
unlistenUpperKeepAlive() {
const {identification, eventEmitter} = this.props._identificationContextProps;
if (!identification) {
return;
}
eventEmitter.off([identification, COMMAND.ACTIVATE], this.activate);
eventEmitter.off([identification, COMMAND.UNACTIVATE], this.unactivate);
eventEmitter.off([identification, COMMAND.UNMOUNT], this.unmount);
}
getCombinedKeepAlive = () => {
return this.combinedKeepAlive;
};
render() {
const {
_identificationContextProps: {
identification,
keepAlive: _keepAlive,
getLifecycle,
},
keepAlive,
...wrapperProps
} = this.props;
const {activated} = this.state;
// When the parent KeepAlive component is mounted or unmounted,
// use the keepAlive prop of the parent KeepAlive component.
const newKeepAlive = getKeepAlive(keepAlive);
this.combinedKeepAlive = getLifecycle === undefined || getLifecycle() === LIFECYCLE.UPDATING
? newKeepAlive
: identification
? _keepAlive && newKeepAlive
: newKeepAlive;
return activated
? (
<TriggerLifecycleContainer
{...wrapperProps}
keepAlive={this.combinedKeepAlive}
getCombinedKeepAlive={this.getCombinedKeepAlive}
/>
)
: null;
}
}
let NewComponent = ListenUpperKeepAliveContainer;
if (forwardRef) {
NewComponent = React.forwardRef((props, ref) => (
<ListenUpperKeepAliveContainer
{...props}
forwardedRef={ref}
/>
));
}
NewComponent.displayName = `keepAlive(${displayName})`;
return hoistNonReactStatics(NewComponent, Component);
};
}
\ No newline at end of file
import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import IdentificationContext from '../contexts/IdentificationContext';
import Consumer, {LIFECYCLE} from '../components/Consumer';
import {START_MOUNTING_DOM} from '../components/Provider';
import Consumer from '../components/Consumer';
import {START_MOUNTING_DOM, LIFECYCLE} from '../components/Provider';
import md5 from './md5';
import noop from './noop';
import getContextIdentificationByFiberNode from './getContextIdentificationByFiberNode';
import withIdentificationContextConsumer from './withIdentificationContextConsumer';
import withKeepAliveContextConsumer from './withKeepAliveContextConsumer';
import withIdentificationContextConsumer, {IIdentificationContextComponentProps} from './withIdentificationContextConsumer';
import withKeepAliveContextConsumer, {IKeepAliveContextComponentProps} from './withKeepAliveContextConsumer';
import changePositionByComment from './changePositionByComment';
import shallowEqual from './shallowEqual';
import getDisplayName from './getDisplayName';
import getKeepAlive from './getKeepAlive';
export const COMMAND = {
UNACTIVATE: 'unactivate',
UNMOUNT: 'unmount',
ACTIVATE: 'activate',
};
export enum COMMAND {
UNACTIVATE = 'unactivate',
UNMOUNT = 'unmount',
ACTIVATE = 'activate',
}
export interface IOptions {
name?: string;
forwardRef?: boolean;
}
export const keepAliveDisplayName = 'keepAlive';
interface IKeepAliveDecorativeComponentProps {
keepAlive?: boolean;
}
export default function keepAlive({
interface IListenUpperKeepAliveContainerProps extends IKeepAliveDecorativeComponentProps, IIdentificationContextComponentProps, IKeepAliveContextComponentProps {
forwardedRef?: React.Ref<{}>;
}
interface IListenUpperKeepAliveContainerState {
activated: boolean;
}
interface ITriggerLifecycleContainerProps extends IKeepAliveContextComponentProps {
forwardedRef?: React.Ref<{}>;
keepAlive: boolean;
getCombinedKeepAlive: () => boolean;
}
interface IComponentProps {
_container: object;
keepAlive: boolean;
ref: React.Ref<{}>;
}
export default function keepAliveDecorator({
name,
forwardRef = false,
} = {}) {
return Component => {
}: IOptions = {}) {
return (Component: React.ComponentType<IComponentProps>) => {
const {
componentDidMount = noop,
componentDidUpdate = noop,
......@@ -31,7 +62,7 @@ export default function keepAlive({
componentWillUnactivate = noop,
componentWillUnmount = noop,
} = Component.prototype;
const displayName = name || getDisplayName(Component);
const displayName = (name || getDisplayName(Component)) as any;
if (!displayName) {
throw new Error('Each component must have a name, which can be the component\'s displayName or name static property. You can also configure name when keepAlive decorates the component.');
......@@ -88,7 +119,7 @@ export default function keepAlive({
}
};
function mount() {
function mount(this: any) {
const {
_container: {
cache,
......@@ -102,7 +133,7 @@ export default function keepAlive({
changePositionByComment(identification, renderElement, storeElement);
}
function unmount() {
function unmount(this: any) {
const {
_container: {
identification,
......@@ -119,21 +150,21 @@ export default function keepAlive({
}
}
class TriggerLifecycleContainer extends React.PureComponent {
identification = null;
class TriggerLifecycleContainer extends React.PureComponent<ITriggerLifecycleContainerProps> {
private identification: string;
ref = null;
private ref: any;
activated = false;
private activated = false;
ifStillActivate = false;
private ifStillActivate = false;
// Let the lifecycle of the cached component be called normally.
needActivate = true;
private needActivate = true;
lifecycle = LIFECYCLE.MOUNTED;
private lifecycle = LIFECYCLE.MOUNTED;
componentDidMount() {
public componentDidMount() {
if (!this.ifStillActivate) {
this.activate();
}
......@@ -149,13 +180,13 @@ export default function keepAlive({
}
}
componentDidCatch() {
public componentDidCatch() {
if (!this.activated) {
this.activate();
}
}
componentWillUnmount() {
public componentWillUnmount() {
const {
getCombinedKeepAlive,
_keepAliveContextProps: {
......@@ -180,57 +211,60 @@ export default function keepAlive({
}
}
activate = () => {
private activate = () => {
this.activated = true;
};
}
reactivate = () => {
private reactivate = () => {
this.ifStillActivate = false;
this.forceUpdate();
};
}
isNeedActivate = () => {
private isNeedActivate = () => {
return this.needActivate;
};
}
notNeedActivate = () => {
private notNeedActivate = () => {
this.needActivate = false;
};
}
getLifecycle = () => {
private getLifecycle = () => {
return this.lifecycle;
};
}
setLifecycle = lifecycle => {
private setLifecycle = (lifecycle: LIFECYCLE) => {
this.lifecycle = lifecycle;
};
}
setRef = ref => {
private setRef = (ref: any) => {
this.ref = ref;
const {
forwardedRef,
} = this.props;
if (forwardedRef) {
forwardedRef(ref);
(forwardedRef as any)(ref);
}
}
};
render() {
public render() {
if (!this.identification) {
// We need to generate a corresponding unique identifier based on the information of the component.
const {providerIdentification, cache} = this.props._keepAliveContextProps;
const {
providerIdentification,
cache,
} = this.props._keepAliveContextProps;
const {
paths,
globalKey,
typeNames,
} = getContextIdentificationByFiberNode(this._reactInternalFiber);
} = getContextIdentificationByFiberNode((this as any)._reactInternalFiber);
this.identification = md5(
`${providerIdentification}${displayName}${globalKey ? `${globalKey}${typeNames}` : paths}`,
);
// The last activated component must be unactivated before it can be activated again.
const currentCache = cache[this.identification];
if (currentCache) {
this.ifStillActivate = currentCache.activated;
this.ifStillActivate = currentCache.activated as boolean;
currentCache.ifStillActivate = this.ifStillActivate;
currentCache.reactivate = this.reactivate;
}
......@@ -248,6 +282,7 @@ export default function keepAlive({
const {
keepAlive,
forwardedRef,
getCombinedKeepAlive,
_keepAliveContextProps: {
isExisted,
storeElement,
......@@ -298,16 +333,22 @@ export default function keepAlive({
}
}
@withKeepAliveContextConsumer
@withIdentificationContextConsumer
class ListenUpperKeepAliveContainer extends React.Component {
combinedKeepAlive = this.props.keepAlive;
class ListenUpperKeepAliveContainer extends React.Component<IListenUpperKeepAliveContainerProps, IListenUpperKeepAliveContainerState> {
public static displayName = `${keepAliveDisplayName}(${displayName})`;
private combinedKeepAlive: boolean;
state = {
public state = {
activated: true,
};
shouldComponentUpdate(nextProps, nextState) {
private activate: () => void;
private unactivate: () => void;
private unmount: () => void;
public shouldComponentUpdate(nextProps: IListenUpperKeepAliveContainerProps, nextState: IListenUpperKeepAliveContainerState) {
if (this.state.activated !== nextState.activated) {
return true;
}
......@@ -317,31 +358,31 @@ export default function keepAlive({
...rest
} = this.props;
const {
_keepAliveContextProps: _nextKeepAliveContextProps,
_identificationContextProps: _nextIdentificationContextProps,
_keepAliveContextProps: nextKeepAliveContextProps,
_identificationContextProps: nextIdentificationContextProps,
...nextRest
} = nextProps;
if (!shallowEqual(rest, nextRest)) {
return true;
}
if (
!shallowEqual(_keepAliveContextProps, _nextKeepAliveContextProps) ||
!shallowEqual(_identificationContextProps, _nextIdentificationContextProps)
!shallowEqual(_keepAliveContextProps, nextKeepAliveContextProps) ||
!shallowEqual(_identificationContextProps, nextIdentificationContextProps)
) {
return true;
}
return false;
}
componentDidMount() {
public componentDidMount() {
this.listenUpperKeepAlive();
}
componentWillUnmount() {
public componentWillUnmount() {
this.unlistenUpperKeepAlive();
}
listenUpperKeepAlive() {
private listenUpperKeepAlive() {
const {identification, eventEmitter} = this.props._identificationContextProps;
if (!identification) {
return;
......@@ -363,7 +404,7 @@ export default function keepAlive({
);
}
unlistenUpperKeepAlive() {
private unlistenUpperKeepAlive() {
const {identification, eventEmitter} = this.props._identificationContextProps;
if (!identification) {
return;
......@@ -373,28 +414,34 @@ export default function keepAlive({
eventEmitter.off([identification, COMMAND.UNMOUNT], this.unmount);
}
getCombinedKeepAlive = () => {
private getCombinedKeepAlive = () => {
return this.combinedKeepAlive;
};
}
render() {
public render() {
const {
_identificationContextProps: {
identification,
keepAlive: _keepAlive,
keepAlive: upperKeepAlive,
getLifecycle,
},
keepAlive,
...wrapperProps
} = this.props;
const {activated} = this.state;
const {
_keepAliveContextProps: {
include,
exclude,
},
} = wrapperProps;
// When the parent KeepAlive component is mounted or unmounted,
// use the keepAlive prop of the parent KeepAlive component.
const newKeepAlive = getKeepAlive(keepAlive);
const newKeepAlive = getKeepAlive(displayName, include, exclude, keepAlive);
this.combinedKeepAlive = getLifecycle === undefined || getLifecycle() === LIFECYCLE.UPDATING
? newKeepAlive
: identification
? _keepAlive && newKeepAlive
? upperKeepAlive && newKeepAlive
: newKeepAlive;
return activated
? (
......@@ -408,16 +455,19 @@ export default function keepAlive({
}
}
let NewComponent = ListenUpperKeepAliveContainer;
const ListenUpperKeepAliveContainerHOC: any = withKeepAliveContextConsumer(
withIdentificationContextConsumer(ListenUpperKeepAliveContainer)
);
let NewComponent: React.ComponentType<IKeepAliveDecorativeComponentProps> = ListenUpperKeepAliveContainerHOC;
if (forwardRef) {
NewComponent = React.forwardRef((props, ref) => (
<ListenUpperKeepAliveContainer
<ListenUpperKeepAliveContainerHOC
{...props}
forwardedRef={ref}
/>
));
}
NewComponent.displayName = `keepAlive(${displayName})`;
return hoistNonReactStatics(NewComponent, Component);
};
}
import md5 from 'js-md5';
import {prefix} from './createUniqueIdentification';
export default function createMD5(string = '', length = 6) {
return `${prefix}-${md5(string).substr(0, length)}`;
export default function createMD5(value: string = '', length = 6) {
return `${prefix}-${md5(value).substr(0, length)}`;
}
export default function noop() {};
\ No newline at end of file
const noop = () => undefined;
export default noop;
/**
* From react
*/
function is(x, y) {
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
......@@ -9,7 +9,7 @@ function is(x, y) {
const hasOwnProperty = Object.prototype.hasOwnProperty;
function shallowEqual(objA, objB) {
function shallowEqual(objA: object, objB: object) {
if (is(objA, objB)) {
return true;
}
......@@ -31,10 +31,10 @@ function shallowEqual(objA, objB) {
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
for (const key of keysA) {
if (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
!hasOwnProperty.call(objB, key) ||
!is(objA[key], objB[key])
) {
return false;
}
......
......@@ -2,10 +2,23 @@ import React from 'react';
import IdentificationContext from '../contexts/IdentificationContext';
import getDisplayName from './getDisplayName';
export default function withIdentificationContextConsumer(Component) {
const NewComponent = props => (
export interface IIdentificationContextProps {
identification: string;
eventEmitter: any;
keepAlive: boolean;
activated: boolean;
getLifecycle: () => number;
isExisted: () => boolean;
}
export interface IIdentificationContextComponentProps {
_identificationContextProps: IIdentificationContextProps;
}
export default function withIdentificationContextConsumer<P = any>(Component: React.ComponentType<IIdentificationContextComponentProps & P>) {
const NewComponent = (props: P) => (
<IdentificationContext.Consumer>
{contextProps => <Component _identificationContextProps={contextProps || {}} {...props} />}
{(contextProps: IIdentificationContextProps) => <Component _identificationContextProps={contextProps || {}} {...props} />}
</IdentificationContext.Consumer>
);
......
import React from 'react';
import {IKeepAliveProviderImpl, IKeepAliveProviderProps} from '../components/Provider';
import KeepAliveContext from '../contexts/KeepAliveContext';
import getDisplayName from './getDisplayName';
export default function withKeepAliveContextConsumer(Component) {
const NewComponent = (props, ref) => (
type IKeepAliveContextProps = IKeepAliveProviderImpl & IKeepAliveProviderProps;
export interface IKeepAliveContextComponentProps {
_keepAliveContextProps: IKeepAliveContextProps;
}
export default function withKeepAliveContextConsumer<P = any>(Component: React.ComponentType<IKeepAliveContextComponentProps & P>) {
const NewComponent = (props: P) => (
<KeepAliveContext.Consumer>
{contextProps => <Component _keepAliveContextProps={contextProps || {}} {...props} ref={ref} />}
{(contextProps: IKeepAliveContextProps) => <Component _keepAliveContextProps={contextProps || {}} {...props} />}
</KeepAliveContext.Consumer>
);
NewComponent.displayName = `withKeepAliveContextConsumer(${getDisplayName(Component)})`;
return React.forwardRef(NewComponent);
return NewComponent;
}
......@@ -6,7 +6,7 @@
"esModuleInterop": true,
"target": "es5",
"lib": ["es6", "dom"],
"allowJs": true,
"declaration": true,
"sourceMap": false,
"jsx": "react",
"moduleResolution": "node",
......
......@@ -13,6 +13,8 @@
"max-classes-per-file": false,
"no-unsafe-finally": false,
"object-literal-sort-keys": false,
"space-before-function-paren": false,
"no-shadowed-variable": false,
"arrow-parens": [true, "ban-single-arg-parens"]
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment