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 @@ ...@@ -892,6 +892,20 @@
"to-fast-properties": "^2.0.0" "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": { "@types/node": {
"version": "10.12.29", "version": "10.12.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.29.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.29.tgz",
...@@ -914,6 +928,15 @@ ...@@ -914,6 +928,15 @@
"csstype": "^2.2.0" "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": { "@webassemblyjs/ast": {
"version": "1.8.5", "version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
"license": "MIT", "license": "MIT",
"sideEffects": false, "sideEffects": false,
"dependencies": { "dependencies": {
"@types/js-md5": "^0.4.2",
"hoist-non-react-statics": "^3.3.0", "hoist-non-react-statics": "^3.3.0",
"js-md5": "^0.7.3" "js-md5": "^0.7.3"
}, },
...@@ -43,8 +44,10 @@ ...@@ -43,8 +44,10 @@
"@babel/plugin-proposal-decorators": "^7.3.0", "@babel/plugin-proposal-decorators": "^7.3.0",
"@babel/preset-env": "^7.3.4", "@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@types/hoist-non-react-statics": "^3.0.1",
"@types/node": "^10.12.21", "@types/node": "^10.12.21",
"@types/react": "^16.8.1", "@types/react": "^16.8.1",
"@types/react-dom": "^16.8.2",
"babel": "^6.23.0", "babel": "^6.23.0",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"codecov": "^3.2.0", "codecov": "^3.2.0",
......
...@@ -2,42 +2,50 @@ import React from 'react'; ...@@ -2,42 +2,50 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import noop from '../utils/noop'; import noop from '../utils/noop';
export default class Comment extends React.PureComponent { interface IReactCommentProps {
parentNode = null; onLoaded: () => void;
currentNode = null; }
commentNode = null;
content = null; class ReactComment extends React.PureComponent<IReactCommentProps> {
public static defaultProps = {
componentDidMount() { onLoaded: noop,
const node = ReactDOM.findDOMNode(this); };
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(); const commentNode = this.createComment();
this.commentNode = commentNode; this.commentNode = commentNode;
this.currentNode = node; this.currentNode = node;
this.parentNode = node.parentNode; this.parentNode = node.parentNode as Node;
this.parentNode.replaceChild(commentNode, node); this.parentNode.replaceChild(commentNode, node);
ReactDOM.unmountComponentAtNode(node); ReactDOM.unmountComponentAtNode(node);
this.props.onLoaded(); this.props.onLoaded();
} }
componentWillUnmount() { public componentWillUnmount() {
this.parentNode.replaceChild(this.currentNode, this.commentNode); this.parentNode.replaceChild(this.currentNode, this.commentNode);
} }
createComment() { private createComment() {
let content = this.props.children; let content = this.props.children;
if (typeof content !== 'string') { if (typeof content !== 'string') {
content = ''; content = '';
} }
content = content.trim(); this.content = (content as string).trim();
this.content = content; return document.createComment(this.content);
return document.createComment(content);
} }
render() { public render() {
return <div />; return <div />;
} }
} }
Comment.defaultProps = { export default ReactComment;
onLoaded: noop,
};
\ No newline at end of file
import React from 'react'; import React from 'react';
import Comment from './Comment'; import Comment from './Comment';
import {LIFECYCLE, ICache, ICacheItem} from './Provider';
import findDOMNodeByFiberNode from '../utils/findDOMNodeByFiberNode'; import findDOMNodeByFiberNode from '../utils/findDOMNodeByFiberNode';
import createUniqueIdentification from '../utils/createUniqueIdentification'; import createUniqueIdentification from '../utils/createUniqueIdentification';
export const LIFECYCLE = { interface IConsumerProps {
MOUNTED: 0, children: React.ReactNode;
UPDATING: 1, identification: string;
UNMOUNTED: 2, keepAlive: boolean;
}; cache: ICache;
setCache: (identification: string, value: ICacheItem) => void;
unactivate: (identification: string) => void;
}
class Consumer extends React.PureComponent<IConsumerProps> {
private renderElement: HTMLElement;
class Consumer extends React.PureComponent { private identification: string = this.props.identification;
renderElement = null;
identification = this.props.identification;
// This attribute is designed to prevent duplicates of the identification of KeepAlive components. // This attribute is designed to prevent duplicates of the identification of KeepAlive components.
key = createUniqueIdentification(); private key: string = createUniqueIdentification();
constructor(props) { constructor(props: IConsumerProps, ...args: any) {
super(props); super(props, ...args);
const {cache, setCache, children} = props; const {cache, setCache, children} = props;
if (!cache || !setCache) { if (!cache || !setCache) {
throw new Error('<KeepAlive> component must be in the <Provider> component.'); throw new Error('<KeepAlive> component must be in the <Provider> component.');
...@@ -26,14 +30,14 @@ class Consumer extends React.PureComponent { ...@@ -26,14 +30,14 @@ class Consumer extends React.PureComponent {
React.Children.only(children); React.Children.only(children);
} }
componentDidMount() { public componentDidMount() {
const { const {
setCache, setCache,
children, children,
keepAlive, keepAlive,
} = this.props; } = this.props;
const {_reactInternalFiber} = this; const {_reactInternalFiber} = this as any;
this.renderElement = findDOMNodeByFiberNode(_reactInternalFiber); this.renderElement = findDOMNodeByFiberNode(_reactInternalFiber) as HTMLElement;
setCache(this.identification, { setCache(this.identification, {
children, children,
keepAlive, keepAlive,
...@@ -44,9 +48,9 @@ class Consumer extends React.PureComponent { ...@@ -44,9 +48,9 @@ class Consumer extends React.PureComponent {
}); });
} }
componentDidUpdate() { public componentDidUpdate() {
const { const {
setCache, setCache,
children, children,
keepAlive, keepAlive,
} = this.props; } = this.props;
...@@ -57,15 +61,15 @@ class Consumer extends React.PureComponent { ...@@ -57,15 +61,15 @@ class Consumer extends React.PureComponent {
}); });
} }
componentWillUnmount() { public componentWillUnmount() {
const {unactivate} = this.props; const {unactivate} = this.props;
unactivate(this.identification); unactivate(this.identification);
} }
render() { public render() {
const {identification} = this; const {identification} = this;
return <Comment>{identification}</Comment>; return <Comment>{identification}</Comment>;
} }
} }
export default Consumer; export default Consumer;
\ No newline at end of file
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Comment from './Comment'; import Comment from './Comment';
import {LIFECYCLE} from './Consumer';
import KeepAliveContext from '../contexts/KeepAliveContext'; import KeepAliveContext from '../contexts/KeepAliveContext';
import createEventEmitter from '../utils/createEventEmitter'; import createEventEmitter from '../utils/createEventEmitter';
import createUniqueIdentification from '../utils/createUniqueIdentification'; import createUniqueIdentification from '../utils/createUniqueIdentification';
...@@ -10,79 +9,119 @@ import createStoreElement from '../utils/createStoreElement'; ...@@ -10,79 +9,119 @@ import createStoreElement from '../utils/createStoreElement';
export const keepAliveProviderTypeName = 'KeepAliveProvider'; export const keepAliveProviderTypeName = 'KeepAliveProvider';
export const START_MOUNTING_DOM = 'startMountingDOM'; 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 // TODO: include max exclude
export default class KeepAliveProvider extends React.PureComponent { export default class KeepAliveProvider extends React.PureComponent<IKeepAliveProviderProps> implements IKeepAliveProviderImpl {
storeElement = createStoreElement(); public static displayName = keepAliveProviderTypeName;
public storeElement = createStoreElement();
// Sometimes data that changes with setState cannot be synchronized, so force refresh // 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) { if (this.needRerender) {
this.needRerender = false; this.needRerender = false;
this.forceUpdate(); this.forceUpdate();
} }
} }
componentWillUnmount() { public componentWillUnmount() {
this.eventEmitter.clear(); this.eventEmitter.clear();
this.existed = false; this.existed = false;
document.body.removeChild(this.storeElement); document.body.removeChild(this.storeElement);
} }
isExisted = () => { public isExisted = () => {
return this.existed; return this.existed;
}; }
setCache = (identification, value) => { public setCache = (identification: string, value: ICacheItem) => {
const {cache, keys} = this; const {cache, keys} = this;
const {key} = cache[identification] || {}; const currentCache = cache[identification];
if (!key) { const key = currentCache && currentCache.key;
keys.push(identification); if (key && value.key && key !== (value.key as unknown)) {
// this.shiftKey();
}
if (key && value.key && key !== value.key) {
throw new Error('Cached components have duplicates.'); throw new Error('Cached components have duplicates.');
} }
if (!currentCache) {
keys.push(identification);
}
this.cache[identification] = { this.cache[identification] = {
...cache[identification], ...currentCache,
...value, ...value,
}; };
this.forceUpdate(); 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; const {cache} = this;
this.cache[identification] = { this.cache[identification] = {
...cache[identification], ...cache[identification],
...@@ -91,36 +130,35 @@ export default class KeepAliveProvider extends React.PureComponent { ...@@ -91,36 +130,35 @@ export default class KeepAliveProvider extends React.PureComponent {
lifecycle: LIFECYCLE.UNMOUNTED, lifecycle: LIFECYCLE.UNMOUNTED,
}; };
this.forceUpdate(); this.forceUpdate();
}; }
startMountingDOM = identification => { private startMountingDOM = (identification: string) => {
this.eventEmitter.emit([identification, START_MOUNTING_DOM]); this.eventEmitter.emit([identification, START_MOUNTING_DOM]);
}; }
render() { public render() {
const { const {
cache, cache,
keys,
providerIdentification, providerIdentification,
isExisted, isExisted,
setCache, setCache,
unactivate, unactivate,
shiftKey,
storeElement, storeElement,
eventEmitter, eventEmitter,
} = this; } = this;
const { const {
children, children: innerChildren,
include, include,
exclude, exclude,
} = this.props; } = this.props;
return ( return (
<KeepAliveContext.Provider <KeepAliveContext.Provider
value={{ value={{
cache, cache,
providerIdentification, providerIdentification,
isExisted, isExisted,
setCache, setCache,
shiftKey,
unactivate, unactivate,
storeElement, storeElement,
eventEmitter, eventEmitter,
...@@ -129,19 +167,15 @@ export default class KeepAliveProvider extends React.PureComponent { ...@@ -129,19 +167,15 @@ export default class KeepAliveProvider extends React.PureComponent {
}} }}
> >
<React.Fragment> <React.Fragment>
{children} {innerChildren}
{ {
Object.entries(cache).map(( keys.map(identification => {
[
identification,
{
keepAlive,
children,
lifecycle,
},
],
) => {
const currentCache = cache[identification]; const currentCache = cache[identification];
const {
keepAlive,
children,
lifecycle,
} = currentCache;
let cacheChildren = children; let cacheChildren = children;
if (lifecycle === LIFECYCLE.MOUNTED && !keepAlive) { if (lifecycle === LIFECYCLE.MOUNTED && !keepAlive) {
// If the cache was last enabled, then the components of this keepAlive package are used, // If the cache was last enabled, then the components of this keepAlive package are used,
...@@ -162,7 +196,7 @@ export default class KeepAliveProvider extends React.PureComponent { ...@@ -162,7 +196,7 @@ export default class KeepAliveProvider extends React.PureComponent {
<React.Fragment> <React.Fragment>
<Comment>{identification}</Comment> <Comment>{identification}</Comment>
{cacheChildren} {cacheChildren}
<Comment <Comment
onLoaded={() => this.startMountingDOM(identification)} onLoaded={() => this.startMountingDOM(identification)}
>{identification}</Comment> >{identification}</Comment>
</React.Fragment> </React.Fragment>
...@@ -177,4 +211,4 @@ export default class KeepAliveProvider extends React.PureComponent { ...@@ -177,4 +211,4 @@ export default class KeepAliveProvider extends React.PureComponent {
</KeepAliveContext.Provider> </KeepAliveContext.Provider>
); );
} }
} }
\ No newline at end of file
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;
...@@ -6,4 +6,4 @@ export { ...@@ -6,4 +6,4 @@ export {
Provider, Provider,
keepAlive, keepAlive,
bindLifecycle, bindLifecycle,
}; };
\ No newline at end of file
This diff is collapsed.
...@@ -5,7 +5,7 @@ import {COMMAND} from './keepAlive'; ...@@ -5,7 +5,7 @@ import {COMMAND} from './keepAlive';
import withIdentificationContextConsumer from './withIdentificationContextConsumer'; import withIdentificationContextConsumer from './withIdentificationContextConsumer';
import getDisplayName from './getDisplayName'; import getDisplayName from './getDisplayName';
export default function bindLifecycle(Component) { export default function bindLifecycle<P = any>(Component: React.ComponentType<P>) {
const { const {
componentDidMount = noop, componentDidMount = noop,
componentDidUpdate = noop, componentDidUpdate = noop,
...@@ -30,8 +30,8 @@ export default function bindLifecycle(Component) { ...@@ -30,8 +30,8 @@ export default function bindLifecycle(Component) {
componentDidActivate.call(this); componentDidActivate.call(this);
} }
eventEmitter.on( eventEmitter.on(
[identification, COMMAND.ACTIVATE], [identification, COMMAND.ACTIVATE],
this._bindActivate = () => this._needActivate = true, this._bindActivate = () => this._needActivate = true,
true, true,
); );
eventEmitter.on( eventEmitter.on(
...@@ -84,11 +84,11 @@ export default function bindLifecycle(Component) { ...@@ -84,11 +84,11 @@ export default function bindLifecycle(Component) {
const NewComponent = withIdentificationContextConsumer( const NewComponent = withIdentificationContextConsumer(
({ ({
forwardRef, forwardRef,
_identificationContextProps: { _identificationContextProps: {
identification, identification,
eventEmitter, eventEmitter,
activated, activated,
keepAlive, keepAlive,
}, },
...wrapperProps ...wrapperProps
...@@ -105,11 +105,11 @@ export default function bindLifecycle(Component) { ...@@ -105,11 +105,11 @@ export default function bindLifecycle(Component) {
activated, activated,
}} }}
/> />
) )
: null : null
), ),
); );
NewComponent.displayName = `bindLifecycle(${getDisplayName(Component)})`; NewComponent.displayName = `bindLifecycle(${getDisplayName(Component)})`;
return hoistNonReactStatics( return hoistNonReactStatics(
React.forwardRef((props, ref) => ( React.forwardRef((props, ref) => (
...@@ -117,4 +117,4 @@ export default function bindLifecycle(Component) { ...@@ -117,4 +117,4 @@ export default function bindLifecycle(Component) {
)), )),
Component, Component,
); );
}; }
const NODE_TYPES = { enum NODE_TYPES {
ELEMENT: 1, ELEMENT = 1,
COMMENT: 8, COMMENT = 8,
}; }
function findElementsBetweenComments(node, identification) { function findElementsBetweenComments(node: Node, identification: string): Node[] {
const elements = []; const elements = [];
const childNodes = node.childNodes; const childNodes = node.childNodes as any;
let startCommentExist = false; let startCommentExist = false;
for (let i = 0; i < childNodes.length; i++) { for (const child of childNodes) {
const child = childNodes[i];
if ( if (
child.nodeType === NODE_TYPES.COMMENT && child.nodeType === NODE_TYPES.COMMENT &&
child.nodeValue.trim() === identification && child.nodeValue.trim() === identification &&
!startCommentExist !startCommentExist
) { ) {
startCommentExist = true; startCommentExist = true;
...@@ -24,10 +23,9 @@ function findElementsBetweenComments(node, identification) { ...@@ -24,10 +23,9 @@ function findElementsBetweenComments(node, identification) {
return elements; return elements;
} }
function findComment(node, identification) { function findComment(node: Node, identification: string): Node | undefined {
const childNodes = node.childNodes; const childNodes = node.childNodes as any;
for (let i = 0; i < childNodes.length; i++) { for (const child of childNodes) {
const child = childNodes[i];
if ( if (
child.nodeType === NODE_TYPES.COMMENT && child.nodeType === NODE_TYPES.COMMENT &&
child.nodeValue.trim() === identification child.nodeValue.trim() === identification
...@@ -37,7 +35,7 @@ function findComment(node, 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) { if (!presentParentNode || !originalParentNode) {
return; return;
} }
...@@ -46,11 +44,11 @@ export default function changePositionByComment(identification, presentParentNod ...@@ -46,11 +44,11 @@ export default function changePositionByComment(identification, presentParentNod
if (!elementNodes.length || !commentNode) { if (!elementNodes.length || !commentNode) {
return; return;
} }
elementNodes.push(elementNodes[elementNodes.length - 1].nextSibling); elementNodes.push(elementNodes[elementNodes.length - 1].nextSibling as Node);
elementNodes.unshift(elementNodes[0].previousSibling); elementNodes.unshift(elementNodes[0].previousSibling as Node);
// Deleting comment elements when using commet components will result in component uninstallation errors // Deleting comment elements when using commet components will result in component uninstallation errors
for (let i = elementNodes.length - 1; i >= 0; i--) { for (let i = elementNodes.length - 1; i >= 0; i--) {
presentParentNode.insertBefore(elementNodes[i], commentNode); presentParentNode.insertBefore(elementNodes[i], commentNode);
} }
originalParentNode.appendChild(commentNode); originalParentNode.appendChild(commentNode);
} }
\ No newline at end of file
type EventNames = string | string[];
type Listener = (...args: any) => void;
export default function createEventEmitter() { export default function createEventEmitter() {
let events = Object.create(null); let events = Object.create(null);
function on(eventNames, listener, direction = false) { function on(eventNames: EventNames, listener: Listener, direction = false) {
eventNames = getEventNames(eventNames); eventNames = getEventNames(eventNames);
let current = events; let current = events;
const maxIndex = eventNames.length - 1; const maxIndex = eventNames.length - 1;
...@@ -9,7 +13,7 @@ export default function createEventEmitter() { ...@@ -9,7 +13,7 @@ export default function createEventEmitter() {
const key = eventNames[i]; const key = eventNames[i];
if (!current[key]) { if (!current[key]) {
current[key] = i === maxIndex ? [] : {}; current[key] = i === maxIndex ? [] : {};
}; }
current = current[key]; current = current[key];
} }
if (!Array.isArray(current)) { if (!Array.isArray(current)) {
...@@ -22,41 +26,43 @@ export default function createEventEmitter() { ...@@ -22,41 +26,43 @@ export default function createEventEmitter() {
} }
} }
function off(eventNames, listener) { function off(eventNames: EventNames, listener: Listener) {
const listeners = getListeners(eventNames); const listeners = getListeners(eventNames);
if (!listeners) { if (!listeners) {
return; return;
} }
const matchIndex = listeners.findIndex(v => v === listener); const matchIndex = listeners.findIndex((v: Listener) => v === listener);
if (matchIndex !== -1) { if (matchIndex !== -1) {
listeners.splice(matchIndex, 1); listeners.splice(matchIndex, 1);
} }
} }
function removeAllListeners(eventNames) { function removeAllListeners(eventNames: EventNames) {
const listeners = getListeners(eventNames); const listeners = getListeners(eventNames);
if (!listeners) { if (!listeners) {
return; return;
} }
eventNames = getEventNames(eventNames); eventNames = getEventNames(eventNames);
const lastEventName = eventNames.pop(); const lastEventName = eventNames.pop();
const event = eventNames.reduce((obj, key) => obj[key], events); if (lastEventName) {
event[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); const listeners = getListeners(eventNames);
if (!listeners) { if (!listeners) {
return; return;
} }
for (let i = 0; i < listeners.length; i++) { for (const listener of listeners) {
if (listeners[i]) { if (listener) {
listeners[i](...args); listener(...args);
} }
} }
} }
function listenerCount(eventNames) { function listenerCount(eventNames: EventNames) {
const listeners = getListeners(eventNames); const listeners = getListeners(eventNames);
return listeners ? listeners.length : 0; return listeners ? listeners.length : 0;
} }
...@@ -65,14 +71,16 @@ export default function createEventEmitter() { ...@@ -65,14 +71,16 @@ export default function createEventEmitter() {
events = Object.create(null); events = Object.create(null);
} }
function getListeners(eventNames) { function getListeners(eventNames: EventNames): Listener[] | undefined {
eventNames = getEventNames(eventNames); eventNames = getEventNames(eventNames);
try { try {
return eventNames.reduce((obj, key) => obj[key], events); return eventNames.reduce((obj, key) => obj[key], events);
} catch (e) {} } catch (e) {
return;
}
} }
function getEventNames(eventNames) { function getEventNames(eventNames: EventNames): string[] {
if (!eventNames) { if (!eventNames) {
throw new Error('Must exist event name.'); throw new Error('Must exist event name.');
} }
...@@ -90,4 +98,4 @@ export default function createEventEmitter() { ...@@ -90,4 +98,4 @@ export default function createEventEmitter() {
listenerCount, listenerCount,
removeAllListeners, removeAllListeners,
}; };
}; }
\ No newline at end of file
import {prefix} from './createUniqueIdentification'; import {prefix} from './createUniqueIdentification';
export default function createStoreElement() { export default function createStoreElement(): HTMLElement {
const keepAliveDOM = document.createElement('div'); const keepAliveDOM = document.createElement('div');
keepAliveDOM.dataset.type = prefix; keepAliveDOM.dataset.type = prefix;
keepAliveDOM.style.display = 'none'; keepAliveDOM.style.display = 'none';
document.body.appendChild(keepAliveDOM); document.body.appendChild(keepAliveDOM);
return keepAliveDOM; return keepAliveDOM;
} }
\ No newline at end of file
...@@ -10,8 +10,8 @@ export const prefix = 'keep-alive'; ...@@ -10,8 +10,8 @@ export const prefix = 'keep-alive';
*/ */
export default function createUniqueIdentification(length = 6) { export default function createUniqueIdentification(length = 6) {
const strings = []; 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); strings[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
} }
return `${prefix}-${strings.join('')}`; return `${prefix}-${strings.join('')}`;
} }
\ No newline at end of file
export default function findDOMNodeByFiberNode(fiberNode) { export default function findDOMNodeByFiberNode(fiberNode: any): HTMLElement | null {
if (!fiberNode) { if (!fiberNode) {
return null; return null;
} }
const { const {
stateNode, stateNode,
return: parent, return: parent,
} = fiberNode; } = fiberNode;
if (!parent) { if (!parent) {
...@@ -13,4 +13,4 @@ export default function findDOMNodeByFiberNode(fiberNode) { ...@@ -13,4 +13,4 @@ export default function findDOMNodeByFiberNode(fiberNode) {
return stateNode; return stateNode;
} }
return findDOMNodeByFiberNode(parent); return findDOMNodeByFiberNode(parent);
} }
\ No newline at end of file
import React from 'react';
import {keepAliveProviderTypeName} from '../components/Provider'; import {keepAliveProviderTypeName} from '../components/Provider';
import {keepAliveDisplayName} from './keepAlive';
export default function getContextIdentificationByFiberNode(fiberNode) { export default function getContextIdentificationByFiberNode(fiberNode: any) {
let globalKey = null; let globalKey: React.Key | null = null;
let typeNames = ''; let typeNames = '';
function getPathsByFiberNode(fiberNode) { function getPathsByFiberNode(fiberNode: any) {
if (!fiberNode) { if (!fiberNode) {
return ''; return '';
} }
...@@ -12,12 +14,12 @@ export default function getContextIdentificationByFiberNode(fiberNode) { ...@@ -12,12 +14,12 @@ export default function getContextIdentificationByFiberNode(fiberNode) {
key, key,
index, index,
} = fiberNode; } = fiberNode;
let typeName = type && type.name ? type.name : ''; const typeName = type && type.displayName;
if (typeName === keepAliveProviderTypeName) { if (typeName === keepAliveProviderTypeName) {
return ''; return '';
} }
const joinName = getPathsByFiberNode(fiberNode.return); const joinName: string = getPathsByFiberNode(fiberNode.return);
if (type && type.displayName && type.displayName.indexOf('keepAlive') !== -1) { if (type && type.displayName && type.displayName.indexOf(keepAliveDisplayName) !== -1) {
if (!globalKey) { if (!globalKey) {
globalKey = key; globalKey = key;
} }
...@@ -31,4 +33,4 @@ export default function getContextIdentificationByFiberNode(fiberNode) { ...@@ -31,4 +33,4 @@ export default function getContextIdentificationByFiberNode(fiberNode) {
globalKey, globalKey,
typeNames, typeNames,
}; };
} }
\ No newline at end of file
export default function getDisplayName(Component) { import React from 'react';
export default function getDisplayName(Component: React.ComponentType) {
return Component.displayName || Component.name || null; return Component.displayName || Component.name || null;
} }
\ No newline at end of file
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]';
}
This diff is collapsed.
import md5 from 'js-md5'; import md5 from 'js-md5';
import {prefix} from './createUniqueIdentification'; import {prefix} from './createUniqueIdentification';
export default function createMD5(string = '', length = 6) { export default function createMD5(value: string = '', length = 6) {
return `${prefix}-${md5(string).substr(0, length)}`; return `${prefix}-${md5(value).substr(0, length)}`;
} }
\ No newline at end of file
export default function noop() {};
\ No newline at end of file
const noop = () => undefined;
export default noop;
/** /**
* From react * From react
*/ */
function is(x, y) { function is(x: any, y: any) {
return ( return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare (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) { ...@@ -9,7 +9,7 @@ function is(x, y) {
const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwnProperty = Object.prototype.hasOwnProperty;
function shallowEqual(objA, objB) { function shallowEqual(objA: object, objB: object) {
if (is(objA, objB)) { if (is(objA, objB)) {
return true; return true;
} }
...@@ -31,10 +31,10 @@ function shallowEqual(objA, objB) { ...@@ -31,10 +31,10 @@ function shallowEqual(objA, objB) {
} }
// Test for A's keys different from B. // Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) { for (const key of keysA) {
if ( if (
!hasOwnProperty.call(objB, keysA[i]) || !hasOwnProperty.call(objB, key) ||
!is(objA[keysA[i]], objB[keysA[i]]) !is(objA[key], objB[key])
) { ) {
return false; return false;
} }
...@@ -43,4 +43,4 @@ function shallowEqual(objA, objB) { ...@@ -43,4 +43,4 @@ function shallowEqual(objA, objB) {
return true; return true;
} }
export default shallowEqual; export default shallowEqual;
\ No newline at end of file
...@@ -2,13 +2,26 @@ import React from 'react'; ...@@ -2,13 +2,26 @@ import React from 'react';
import IdentificationContext from '../contexts/IdentificationContext'; import IdentificationContext from '../contexts/IdentificationContext';
import getDisplayName from './getDisplayName'; import getDisplayName from './getDisplayName';
export default function withIdentificationContextConsumer(Component) { export interface IIdentificationContextProps {
const NewComponent = props => ( 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> <IdentificationContext.Consumer>
{contextProps => <Component _identificationContextProps={contextProps || {}} {...props} />} {(contextProps: IIdentificationContextProps) => <Component _identificationContextProps={contextProps || {}} {...props} />}
</IdentificationContext.Consumer> </IdentificationContext.Consumer>
); );
NewComponent.displayName = `withIdentificationContextConsumer(${getDisplayName(Component)})`; NewComponent.displayName = `withIdentificationContextConsumer(${getDisplayName(Component)})`;
return NewComponent; return NewComponent;
} }
\ No newline at end of file
import React from 'react'; import React from 'react';
import {IKeepAliveProviderImpl, IKeepAliveProviderProps} from '../components/Provider';
import KeepAliveContext from '../contexts/KeepAliveContext'; import KeepAliveContext from '../contexts/KeepAliveContext';
import getDisplayName from './getDisplayName'; import getDisplayName from './getDisplayName';
export default function withKeepAliveContextConsumer(Component) { type IKeepAliveContextProps = IKeepAliveProviderImpl & IKeepAliveProviderProps;
const NewComponent = (props, ref) => (
export interface IKeepAliveContextComponentProps {
_keepAliveContextProps: IKeepAliveContextProps;
}
export default function withKeepAliveContextConsumer<P = any>(Component: React.ComponentType<IKeepAliveContextComponentProps & P>) {
const NewComponent = (props: P) => (
<KeepAliveContext.Consumer> <KeepAliveContext.Consumer>
{contextProps => <Component _keepAliveContextProps={contextProps || {}} {...props} ref={ref} />} {(contextProps: IKeepAliveContextProps) => <Component _keepAliveContextProps={contextProps || {}} {...props} />}
</KeepAliveContext.Consumer> </KeepAliveContext.Consumer>
); );
NewComponent.displayName = `withKeepAliveContextConsumer(${getDisplayName(Component)})`; NewComponent.displayName = `withKeepAliveContextConsumer(${getDisplayName(Component)})`;
return React.forwardRef(NewComponent); return NewComponent;
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"target": "es5", "target": "es5",
"lib": ["es6", "dom"], "lib": ["es6", "dom"],
"allowJs": true, "declaration": true,
"sourceMap": false, "sourceMap": false,
"jsx": "react", "jsx": "react",
"moduleResolution": "node", "moduleResolution": "node",
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
"max-classes-per-file": false, "max-classes-per-file": false,
"no-unsafe-finally": false, "no-unsafe-finally": false,
"object-literal-sort-keys": false, "object-literal-sort-keys": false,
"space-before-function-paren": false,
"no-shadowed-variable": false,
"arrow-parens": [true, "ban-single-arg-parens"] "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