Commit 46c0028d authored by Shen Chang's avatar Shen Chang

refactor: Remove the componentDidActivate and componentWillUnmount lifecycles,...

refactor: Remove the componentDidActivate and componentWillUnmount lifecycles, Remove the <KeepAlive
parent 56d781e6
import React from 'react'; import React, {useState} from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { import {
Switch, Switch,
...@@ -11,77 +11,53 @@ import A from './views/A'; ...@@ -11,77 +11,53 @@ import A from './views/A';
import B from './views/B'; import B from './views/B';
import C from './views/C'; import C from './views/C';
class App extends React.Component { function App() {
state = { const [toggle, setToggle] = useState(true);
toggle: true, return (
}; <div>
<ul>
<li>
<Link to="/a">a</Link>
</li>
<li onClick={() => setToggle(true)}>
<Link to="/b">b</Link>
</li>
<li onClick={() => setToggle(false)}>
<Link to="/c">c</Link>
</li>
</ul>
handleClick = () => {
this.setState(({toggle}) => ({
toggle: !toggle,
}));
}
handleClickB = () => {
this.setState({
toggle: true,
});
}
handleClickC = () => {
this.setState({
toggle: false,
});
}
render() {
const {toggle} = this.state;
return (
<div> <div>
<ul> <button onClick={() => setToggle(!toggle)}>toggle({toggle.toString()})</button>
<li> </div>
<Link to="/a">a</Link>
</li>
<li onClick={this.handleClickB}>
<Link to="/b">b</Link>
</li>
<li onClick={this.handleClickC}>
<Link to="/c">c</Link>
</li>
</ul>
<div>
<button onClick={this.handleClick}>toggle({toggle.toString()})</button>
</div>
<Switch> <Switch>
<Route <Route
path="/a" path="/a"
render={() => ( render={() => (
<KeepAlive key="A" disabled={!toggle}> <KeepAlive key="A" disabled={!toggle}>
<A /> <A />
</KeepAlive> </KeepAlive>
)} )}
/> />
<Route <Route
path="/b" path="/b"
render={() => ( render={() => (
<KeepAlive key="B"><B /></KeepAlive> <KeepAlive key="B"><B /></KeepAlive>
)} )}
/> />
<Route <Route
path="/c" path="/c"
render={() => ( render={() => (
<KeepAlive key="C"> <KeepAlive key="C">
<C /> <C />
</KeepAlive> </KeepAlive>
)} )}
/> />
</Switch> </Switch>
</div> </div>
); );
}
} }
ReactDOM.render( ReactDOM.render(
......
import React from 'react'; import React, {useState} from 'react';
import {Provider, KeepAlive} from '../../../es'; import {useKeepAliveEffect} from '../../../es';
class Content extends React.Component { function Test() {
componentWillMount() { const [index, setIndex] = useState(0);
console.log('A Content componentWillMount'); useKeepAliveEffect(() => {
} console.log('activated');
componentDidMount() { return () => {
console.log('A Content componentDidMount'); console.log('unactivated');
} };
});
componentDidActivate() { return (
console.log('A Content componentDidActivate'); <div>
} <div>This is a.</div>
<button onClick={() => setIndex(index + 1)}>click me({index})</button>
componentWillUpdate() { </div>
console.log('A Content componentWillUpdate'); );
}
componentDidUpdate() {
console.log('A Content componentDidUpdate');
}
componentWillUnactivate() {
console.log('A Content componentWillUnactivate');
}
componentWillUnmount() {
console.log('A Content componentWillUnmount');
}
render() {
console.log('A Content render');
console.log(this);
return (
<div>This is a content.</div>
);
}
}
class Test extends React.Component {
state = {
index: 0,
};
componentWillMount() {
console.log('A componentWillMount');
}
componentDidMount() {
console.log('A componentDidMount');
}
componentDidActivate() {
console.log('A componentDidActivate');
}
componentWillUpdate() {
console.log('A componentWillUpdate');
}
componentDidUpdate() {
console.log('A componentDidUpdate');
}
componentWillUnactivate() {
console.log('A componentWillUnactivate');
}
componentWillUnmount() {
console.log('A componentWillUnmount');
}
handleClick = () => {
this.setState(({index}) => ({index: ++index}));
};
render() {
console.log('A render');
return (
<div>
<div>This is a.</div>
<button onClick={this.handleClick}>click me({this.state.index})</button>
{
this.state.index % 2 &&
<Provider>
<KeepAlive key="111">
<Content />
</KeepAlive>
</Provider>
}
</div>
);
}
} }
export default Test; export default Test;
This diff is collapsed.
{ {
"name": "react-keep-alive", "name": "react-keep-alive",
"version": "0.4.3", "version": "1.0.0-alpha.0",
"description": "Package will allow components to maintain their status, to avoid repeated re-rendering.", "description": "Package will allow components to maintain their status, to avoid repeated re-rendering.",
"author": "Shen Chang", "author": "Shen Chang",
"homepage": "https://github.com/Sam618/react-keep-alive", "homepage": "https://github.com/Sam618/react-keep-alive",
"keywords": [ "keywords": [
"react", "react",
"keep-alive" "react-router",
"keep-alive",
"cache"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -57,9 +59,9 @@ ...@@ -57,9 +59,9 @@
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"husky": "^1.3.1", "husky": "^1.3.1",
"jest": "^24.1.0", "jest": "^24.1.0",
"react": "^16.3.0", "react": "^16.8.5",
"react-dom": "^16.3.0", "react-dom": "^16.8.5",
"react-router-dom": "^4.3.1", "react-router-dom": "^5.0.0",
"rimraf": "^2.6.3", "rimraf": "^2.6.3",
"ts-jest": "^23.10.5", "ts-jest": "^23.10.5",
"typescript": "^3.3.1", "typescript": "^3.3.1",
......
import React from 'react'; import React from 'react';
import noop from '../utils/noop'; import {START_MOUNTING_DOM, LIFECYCLE} from './Provider';
import keepAlive, {IKeepAliveComponentProps} from '../utils/keepAlive'; import keepAlive, {COMMAND} from '../utils/keepAliveDecorator';
import changePositionByComment from '../utils/changePositionByComment';
export const keepAliveDisplayName = '$$KeepAlive'; interface IKeepAliveProps {
interface IKeepAliveProps extends IKeepAliveComponentProps {
key: string; key: string;
onActivate?: () => void; disabled?: boolean;
onUnactivate?: () => void; _container: any;
} }
class KeepAlive extends React.PureComponent<IKeepAliveProps> { class KeepAlive extends React.PureComponent<IKeepAliveProps> {
public displayName = keepAliveDisplayName; private bindUnmount: (() => void) | null = null;
public componentDidMount() {
const {
_container,
} = this.props;
const {
notNeedActivate,
identification,
eventEmitter,
} = _container;
notNeedActivate();
const cb = () => {
this.mount();
this.listen();
eventEmitter.off([identification, START_MOUNTING_DOM], cb);
};
eventEmitter.on([identification, START_MOUNTING_DOM], cb);
}
public componentDidUpdate() {
const {
_container,
} = this.props;
const {
notNeedActivate,
isNeedActivate,
} = _container;
if (isNeedActivate()) {
notNeedActivate();
this.mount();
this.listen();
}
}
public static defaultProps = { public componentWillUnmount() {
onActivate: noop, this.unmount();
onUnactivate: noop, this.unlisten();
}; }
private mount() {
const {
_container: {
cache,
identification,
storeElement,
setLifecycle,
},
} = this.props;
const {renderElement} = cache[identification];
setLifecycle(LIFECYCLE.UPDATING);
changePositionByComment(identification, renderElement, storeElement);
}
private 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();
}
}
public componentDidActivate() { private listen() {
(this.props as any).onActivate(); const {
_container: {
identification,
eventEmitter,
},
} = this.props;
eventEmitter.on(
[identification, COMMAND.CURRENT_UNMOUNT],
this.bindUnmount = this.componentWillUnmount.bind(this),
);
} }
public componentWillUnactivate() { private unlisten() {
(this.props as any).onUnactivate(); const {
_container: {
identification,
eventEmitter,
},
} = this.props;
eventEmitter.off([identification, COMMAND.CURRENT_UNMOUNT], this.bindUnmount);
} }
public render() { public render() {
......
...@@ -119,6 +119,7 @@ export default class KeepAliveProvider extends React.PureComponent<IKeepAlivePro ...@@ -119,6 +119,7 @@ export default class KeepAliveProvider extends React.PureComponent<IKeepAlivePro
providerIdentification, providerIdentification,
isExisted, isExisted,
setCache, setCache,
existed,
unactivate, unactivate,
storeElement, storeElement,
eventEmitter, eventEmitter,
...@@ -132,6 +133,8 @@ export default class KeepAliveProvider extends React.PureComponent<IKeepAlivePro ...@@ -132,6 +133,8 @@ export default class KeepAliveProvider extends React.PureComponent<IKeepAlivePro
<KeepAliveContext.Provider <KeepAliveContext.Provider
value={{ value={{
cache, cache,
keys,
existed,
providerIdentification, providerIdentification,
isExisted, isExisted,
setCache, setCache,
......
import React from 'react'; import React from 'react';
const WithKeepAliveContext = React.createContext({}); export interface IIdentificationContextProps {
identification: string;
eventEmitter: any;
keepAlive: boolean;
getLifecycle: () => number;
isExisted: () => boolean;
}
const WithKeepAliveContext = React.createContext<IIdentificationContextProps>({} as any);
export default WithKeepAliveContext; export default WithKeepAliveContext;
import React from 'react'; import React from 'react';
import {IKeepAliveProviderImpl, IKeepAliveProviderProps} from '../components/Provider';
const KeepAliveContext = React.createContext({}); export type IKeepAliveContextProps = IKeepAliveProviderImpl & IKeepAliveProviderProps;
const KeepAliveContext = React.createContext<IKeepAliveContextProps>({} as any);
export default KeepAliveContext; export default KeepAliveContext;
import Provider from './components/Provider'; import Provider from './components/Provider';
import KeepAlive from './components/KeepAlive'; import KeepAlive from './components/KeepAlive';
import bindLifecycle from './utils/bindLifecycle'; import bindLifecycle from './utils/bindLifecycle';
import useKeepAliveEffect from './utils/useKeepAliveEffect';
export { export {
Provider, Provider,
KeepAlive, KeepAlive,
bindLifecycle, bindLifecycle,
useKeepAliveEffect,
}; };
...@@ -2,7 +2,7 @@ import React from 'react'; ...@@ -2,7 +2,7 @@ import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics'; import hoistNonReactStatics from 'hoist-non-react-statics';
import noop from './noop'; import noop from './noop';
import {warn} from './debug'; import {warn} from './debug';
import {COMMAND} from './keepAlive'; import {COMMAND} from './keepAliveDecorator';
import withIdentificationContextConsumer from './withIdentificationContextConsumer'; import withIdentificationContextConsumer from './withIdentificationContextConsumer';
import getDisplayName from './getDisplayName'; import getDisplayName from './getDisplayName';
...@@ -12,8 +12,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P ...@@ -12,8 +12,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
const { const {
componentDidMount = noop, componentDidMount = noop,
componentDidUpdate = noop, componentDidUpdate = noop,
componentDidActivate = noop,
componentWillUnactivate = noop,
componentWillUnmount = noop, componentWillUnmount = noop,
} = WrappedComponent.prototype; } = WrappedComponent.prototype;
...@@ -24,25 +22,12 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P ...@@ -24,25 +22,12 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
_container: { _container: {
identification, identification,
eventEmitter, eventEmitter,
activated,
}, },
keepAlive,
} = this.props; } = this.props;
// Determine whether to execute the componentDidActivate life cycle of the current component based on the activation state of the KeepAlive components this._unmounted = false;
if (!activated && keepAlive !== false) {
componentDidActivate.call(this);
}
eventEmitter.on( eventEmitter.on(
[identification, COMMAND.ACTIVATE], [identification, COMMAND.MOUNT],
this._bindActivate = () => this._needActivate = true, this._bindMount = () => this._needActivate = true,
true,
);
eventEmitter.on(
[identification, COMMAND.UNACTIVATE],
this._bindUnactivate = () => {
componentWillUnactivate.call(this);
this._unmounted = false;
},
true, true,
); );
eventEmitter.on( eventEmitter.on(
...@@ -54,11 +39,13 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P ...@@ -54,11 +39,13 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
true, true,
); );
}; };
WrappedComponent.prototype.componentDidUpdate = function () { WrappedComponent.prototype.componentDidUpdate = function (...args: any) {
componentDidUpdate.call(this);
if (this._needActivate) { if (this._needActivate) {
this._needActivate = false; this._needActivate = false;
componentDidActivate.call(this); this._unmounted = false;
componentDidMount.call(this);
} else {
componentDidUpdate.apply(this, args);
} }
}; };
WrappedComponent.prototype.componentWillUnmount = function () { WrappedComponent.prototype.componentWillUnmount = function () {
...@@ -72,12 +59,8 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P ...@@ -72,12 +59,8 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
}, },
} = this.props; } = this.props;
eventEmitter.off( eventEmitter.off(
[identification, COMMAND.ACTIVATE], [identification, COMMAND.MOUNT],
this._bindActivate, this._bindMount,
);
eventEmitter.off(
[identification, COMMAND.UNACTIVATE],
this._bindUnactivate,
); );
eventEmitter.off( eventEmitter.off(
[identification, COMMAND.UNMOUNT], [identification, COMMAND.UNMOUNT],
...@@ -91,8 +74,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P ...@@ -91,8 +74,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
_identificationContextProps: { _identificationContextProps: {
identification, identification,
eventEmitter, eventEmitter,
activated,
keepAlive,
}, },
...wrapperProps ...wrapperProps
}) => { }) => {
...@@ -107,8 +88,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P ...@@ -107,8 +88,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
_container={{ _container={{
identification, identification,
eventEmitter, eventEmitter,
activated,
keepAlive,
}} }}
/> />
); );
......
import React, {useEffect, useContext} from 'react';
import {warn} from './debug';
import {COMMAND} from './keepAliveDecorator';
import IdentificationContext, {IIdentificationContextProps} from '../contexts/IdentificationContext';
export default function useKeepAliveEffect(effect: React.EffectCallback) {
if (!useEffect) {
warn('[React Keep Alive] useKeepAlive API requires react 16.8 or later.');
}
const {
eventEmitter,
identification,
} = useContext<IIdentificationContextProps>(IdentificationContext);
useEffect(() => {
let bindMount: (() => void) | null = null;
let bindUnmount: (() => void) | null = null;
let effectResult = effect();
let unmounted = false;
eventEmitter.on(
[identification, COMMAND.MOUNT],
bindMount = () => {
effectResult = effect();
unmounted = false;
},
true,
);
eventEmitter.on(
[identification, COMMAND.UNMOUNT],
bindUnmount = () => {
if (effectResult) {
effectResult();
unmounted = true;
}
},
true,
);
return () => {
if (effectResult && !unmounted) {
effectResult();
}
eventEmitter.off(
[identification, COMMAND.MOUNT],
bindMount,
);
eventEmitter.off(
[identification, COMMAND.UNMOUNT],
bindUnmount,
);
};
}, []);
}
import React from 'react'; import React from 'react';
import IdentificationContext from '../contexts/IdentificationContext'; import IdentificationContext, {IIdentificationContextProps} from '../contexts/IdentificationContext';
import getDisplayName from './getDisplayName'; import getDisplayName from './getDisplayName';
export interface IIdentificationContextProps {
identification: string;
eventEmitter: any;
keepAlive: boolean;
activated: boolean;
getLifecycle: () => number;
isExisted: () => boolean;
}
export interface IIdentificationContextConsumerComponentProps { export interface IIdentificationContextConsumerComponentProps {
_identificationContextProps: IIdentificationContextProps; _identificationContextProps: IIdentificationContextProps;
} }
......
import React from 'react'; import React from 'react';
import {IKeepAliveProviderImpl, IKeepAliveProviderProps} from '../components/Provider'; import KeepAliveContext, {IKeepAliveContextProps} from '../contexts/KeepAliveContext';
import KeepAliveContext from '../contexts/KeepAliveContext';
import getDisplayName from './getDisplayName'; import getDisplayName from './getDisplayName';
type IKeepAliveContextProps = IKeepAliveProviderImpl & IKeepAliveProviderProps;
export interface IKeepAliveContextConsumerComponentProps { export interface IKeepAliveContextConsumerComponentProps {
_keepAliveContextProps: IKeepAliveContextProps; _keepAliveContextProps: IKeepAliveContextProps;
} }
......
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