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 {
Switch,
......@@ -11,77 +11,53 @@ import A from './views/A';
import B from './views/B';
import C from './views/C';
class App extends React.Component {
state = {
toggle: true,
};
function App() {
const [toggle, setToggle] = useState(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>
<ul>
<li>
<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>
<button onClick={() => setToggle(!toggle)}>toggle({toggle.toString()})</button>
</div>
<Switch>
<Route
path="/a"
render={() => (
<KeepAlive key="A" disabled={!toggle}>
<A />
</KeepAlive>
)}
/>
<Route
path="/b"
render={() => (
<KeepAlive key="B"><B /></KeepAlive>
<Switch>
<Route
path="/a"
render={() => (
<KeepAlive key="A" disabled={!toggle}>
<A />
</KeepAlive>
)}
/>
<Route
path="/b"
render={() => (
<KeepAlive key="B"><B /></KeepAlive>
)}
/>
<Route
path="/c"
render={() => (
<KeepAlive key="C">
<C />
</KeepAlive>
)}
/>
</Switch>
</div>
);
}
)}
/>
<Route
path="/c"
render={() => (
<KeepAlive key="C">
<C />
</KeepAlive>
)}
/>
</Switch>
</div>
);
}
ReactDOM.render(
......
import React from 'react';
import {Provider, KeepAlive} from '../../../es';
class Content extends React.Component {
componentWillMount() {
console.log('A Content componentWillMount');
}
componentDidMount() {
console.log('A Content componentDidMount');
}
componentDidActivate() {
console.log('A Content componentDidActivate');
}
componentWillUpdate() {
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>
);
}
import React, {useState} from 'react';
import {useKeepAliveEffect} from '../../../es';
function Test() {
const [index, setIndex] = useState(0);
useKeepAliveEffect(() => {
console.log('activated');
return () => {
console.log('unactivated');
};
});
return (
<div>
<div>This is a.</div>
<button onClick={() => setIndex(index + 1)}>click me({index})</button>
</div>
);
}
export default Test;
{
"name": "react-keep-alive",
"version": "0.1.0",
"version": "0.4.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -836,6 +836,15 @@
"@babel/plugin-transform-react-jsx-source": "^7.0.0"
}
},
"@babel/runtime": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.2.tgz",
"integrity": "sha512-7Bl2rALb7HpvXFL7TETNzKSAeBVCPHELzc0C//9FCxN8nsiueWSJBqaF+2oIJScyILStASR/Cx5WMkXGYTiJFA==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"@babel/template": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz",
......@@ -1368,6 +1377,12 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
"dev": true
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
......@@ -2347,6 +2362,12 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true
},
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
"dev": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
......@@ -2403,6 +2424,16 @@
"sha.js": "^2.4.8"
}
},
"create-react-context": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
"integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==",
"dev": true,
"requires": {
"fbjs": "^0.8.0",
"gud": "^1.0.0"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
......@@ -2862,6 +2893,15 @@
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"dev": true,
"requires": {
"iconv-lite": "~0.4.13"
}
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
......@@ -3389,6 +3429,21 @@
"bser": "^2.0.0"
}
},
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"dev": true,
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"figgy-pudding": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
......@@ -4299,6 +4354,12 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"dev": true
},
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==",
"dev": true
},
"handle-thing": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
......@@ -4421,27 +4482,17 @@
"dev": true
},
"history": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
"integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==",
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz",
"integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==",
"dev": true,
"requires": {
"invariant": "^2.2.1",
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^2.2.0",
"value-equal": "^0.4.0",
"warning": "^3.0.0"
},
"dependencies": {
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"dev": true,
"requires": {
"loose-envify": "^1.0.0"
}
}
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^0.4.0"
}
},
"hmac-drbg": {
......@@ -5138,6 +5189,28 @@
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true
},
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"dev": true,
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
},
"dependencies": {
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"dev": true,
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
}
}
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
......@@ -7108,6 +7181,15 @@
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"dev": true,
"requires": {
"asap": "~2.0.3"
}
},
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
......@@ -7308,27 +7390,51 @@
}
},
"react": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.3.tgz",
"integrity": "sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==",
"version": "16.8.5",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.5.tgz",
"integrity": "sha512-daCb9TD6FZGvJ3sg8da1tRAtIuw29PbKZW++NN4wqkbEvxL+bZpaaYb4xuftW/SpXmgacf1skXl/ddX6CdOlDw==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.3"
"scheduler": "^0.13.5"
},
"dependencies": {
"scheduler": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.5.tgz",
"integrity": "sha512-K98vjkQX9OIt/riLhp6F+XtDPtMQhqNcf045vsh+pcuvHq+PHy1xCrH3pq1P40m6yR46lpVvVhKdEOtnimuUJw==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
}
}
},
"react-dom": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.3.tgz",
"integrity": "sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA==",
"version": "16.8.5",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.5.tgz",
"integrity": "sha512-VIEIvZLpFafsfu4kgmftP5L8j7P1f0YThfVTrANMhZUFMDOsA6e0kfR6wxw/8xxKs4NB59TZYbxNdPCDW34x4w==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.3"
"scheduler": "^0.13.5"
},
"dependencies": {
"scheduler": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.5.tgz",
"integrity": "sha512-K98vjkQX9OIt/riLhp6F+XtDPtMQhqNcf045vsh+pcuvHq+PHy1xCrH3pq1P40m6yR46lpVvVhKdEOtnimuUJw==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
}
}
},
"react-is": {
......@@ -7337,40 +7443,36 @@
"integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA=="
},
"react-router": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
"integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.0.tgz",
"integrity": "sha512-6EQDakGdLG/it2x9EaCt9ZpEEPxnd0OCLBHQ1AcITAAx7nCnyvnzf76jKWG1s2/oJ7SSviUgfWHofdYljFexsA==",
"dev": true,
"requires": {
"history": "^4.7.2",
"hoist-non-react-statics": "^2.5.0",
"invariant": "^2.2.4",
"@babel/runtime": "^7.1.2",
"create-react-context": "^0.2.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.1",
"warning": "^4.0.1"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
"dev": true
}
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-router-dom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz",
"integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.0.tgz",
"integrity": "sha512-wSpja5g9kh5dIteZT3tUoggjnsa+TPFHSMrpHXMpFsaHhQkm/JNVGh2jiF9Dkh4+duj4MKCkwO6H08u6inZYgQ==",
"dev": true,
"requires": {
"history": "^4.7.2",
"invariant": "^2.2.4",
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.1",
"react-router": "^4.3.1",
"warning": "^4.0.1"
"prop-types": "^15.6.2",
"react-router": "5.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-test-renderer": {
......@@ -7469,6 +7571,12 @@
"regenerate": "^1.4.0"
}
},
"regenerator-runtime": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==",
"dev": true
},
"regenerator-transform": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz",
......@@ -8653,6 +8761,18 @@
"setimmediate": "^1.0.4"
}
},
"tiny-invariant": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.3.tgz",
"integrity": "sha512-ytQx8T4DL8PjlX53yYzcIC0WhIZbpR0p1qcYjw2pHu3w6UtgWwFJQ/02cnhOnBBhlFx/edUIfcagCaQSe3KMWg==",
"dev": true
},
"tiny-warning": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz",
"integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==",
"dev": true
},
"tmpl": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
......@@ -8836,6 +8956,12 @@
"integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.19",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
"integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==",
"dev": true
},
"uglify-js": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
......@@ -9163,15 +9289,6 @@
"makeerror": "1.0.x"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"dev": true,
"requires": {
"loose-envify": "^1.0.0"
}
},
"watch": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
......@@ -9456,6 +9573,12 @@
"iconv-lite": "0.4.24"
}
},
"whatwg-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==",
"dev": true
},
"whatwg-mimetype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
......
{
"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.",
"author": "Shen Chang",
"homepage": "https://github.com/Sam618/react-keep-alive",
"keywords": [
"react",
"keep-alive"
"react-router",
"keep-alive",
"cache"
],
"repository": {
"type": "git",
......@@ -57,9 +59,9 @@
"html-webpack-plugin": "^3.2.0",
"husky": "^1.3.1",
"jest": "^24.1.0",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-router-dom": "^4.3.1",
"react": "^16.8.5",
"react-dom": "^16.8.5",
"react-router-dom": "^5.0.0",
"rimraf": "^2.6.3",
"ts-jest": "^23.10.5",
"typescript": "^3.3.1",
......
import React from 'react';
import noop from '../utils/noop';
import keepAlive, {IKeepAliveComponentProps} from '../utils/keepAlive';
import {START_MOUNTING_DOM, LIFECYCLE} from './Provider';
import keepAlive, {COMMAND} from '../utils/keepAliveDecorator';
import changePositionByComment from '../utils/changePositionByComment';
export const keepAliveDisplayName = '$$KeepAlive';
interface IKeepAliveProps extends IKeepAliveComponentProps {
interface IKeepAliveProps {
key: string;
onActivate?: () => void;
onUnactivate?: () => void;
disabled?: boolean;
_container: any;
}
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 = {
onActivate: noop,
onUnactivate: noop,
};
public componentWillUnmount() {
this.unmount();
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() {
(this.props as any).onActivate();
private listen() {
const {
_container: {
identification,
eventEmitter,
},
} = this.props;
eventEmitter.on(
[identification, COMMAND.CURRENT_UNMOUNT],
this.bindUnmount = this.componentWillUnmount.bind(this),
);
}
public componentWillUnactivate() {
(this.props as any).onUnactivate();
private unlisten() {
const {
_container: {
identification,
eventEmitter,
},
} = this.props;
eventEmitter.off([identification, COMMAND.CURRENT_UNMOUNT], this.bindUnmount);
}
public render() {
......
......@@ -119,6 +119,7 @@ export default class KeepAliveProvider extends React.PureComponent<IKeepAlivePro
providerIdentification,
isExisted,
setCache,
existed,
unactivate,
storeElement,
eventEmitter,
......@@ -132,6 +133,8 @@ export default class KeepAliveProvider extends React.PureComponent<IKeepAlivePro
<KeepAliveContext.Provider
value={{
cache,
keys,
existed,
providerIdentification,
isExisted,
setCache,
......
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;
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;
import Provider from './components/Provider';
import KeepAlive from './components/KeepAlive';
import bindLifecycle from './utils/bindLifecycle';
import useKeepAliveEffect from './utils/useKeepAliveEffect';
export {
Provider,
KeepAlive,
bindLifecycle,
useKeepAliveEffect,
};
......@@ -2,7 +2,7 @@ import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import noop from './noop';
import {warn} from './debug';
import {COMMAND} from './keepAlive';
import {COMMAND} from './keepAliveDecorator';
import withIdentificationContextConsumer from './withIdentificationContextConsumer';
import getDisplayName from './getDisplayName';
......@@ -12,8 +12,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
const {
componentDidMount = noop,
componentDidUpdate = noop,
componentDidActivate = noop,
componentWillUnactivate = noop,
componentWillUnmount = noop,
} = WrappedComponent.prototype;
......@@ -24,25 +22,12 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
_container: {
identification,
eventEmitter,
activated,
},
keepAlive,
} = this.props;
// Determine whether to execute the componentDidActivate life cycle of the current component based on the activation state of the KeepAlive components
if (!activated && keepAlive !== false) {
componentDidActivate.call(this);
}
this._unmounted = false;
eventEmitter.on(
[identification, COMMAND.ACTIVATE],
this._bindActivate = () => this._needActivate = true,
true,
);
eventEmitter.on(
[identification, COMMAND.UNACTIVATE],
this._bindUnactivate = () => {
componentWillUnactivate.call(this);
this._unmounted = false;
},
[identification, COMMAND.MOUNT],
this._bindMount = () => this._needActivate = true,
true,
);
eventEmitter.on(
......@@ -54,11 +39,13 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
true,
);
};
WrappedComponent.prototype.componentDidUpdate = function () {
componentDidUpdate.call(this);
WrappedComponent.prototype.componentDidUpdate = function (...args: any) {
if (this._needActivate) {
this._needActivate = false;
componentDidActivate.call(this);
this._unmounted = false;
componentDidMount.call(this);
} else {
componentDidUpdate.apply(this, args);
}
};
WrappedComponent.prototype.componentWillUnmount = function () {
......@@ -72,12 +59,8 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
},
} = this.props;
eventEmitter.off(
[identification, COMMAND.ACTIVATE],
this._bindActivate,
);
eventEmitter.off(
[identification, COMMAND.UNACTIVATE],
this._bindUnactivate,
[identification, COMMAND.MOUNT],
this._bindMount,
);
eventEmitter.off(
[identification, COMMAND.UNMOUNT],
......@@ -91,8 +74,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
_identificationContextProps: {
identification,
eventEmitter,
activated,
keepAlive,
},
...wrapperProps
}) => {
......@@ -107,8 +88,6 @@ export default function bindLifecycle<P = any>(Component: React.ComponentClass<P
_container={{
identification,
eventEmitter,
activated,
keepAlive,
}}
/>
);
......
......@@ -2,33 +2,25 @@ import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import IdentificationContext from '../contexts/IdentificationContext';
import Consumer from '../components/Consumer';
import {START_MOUNTING_DOM, LIFECYCLE} from '../components/Provider';
import {LIFECYCLE} from '../components/Provider';
import md5 from './md5';
import noop from './noop';
import {warn} from './debug';
import getKeyByFiberNode from './getKeyByFiberNode';
import withIdentificationContextConsumer, {IIdentificationContextConsumerComponentProps} from './withIdentificationContextConsumer';
import withKeepAliveContextConsumer, {IKeepAliveContextConsumerComponentProps} from './withKeepAliveContextConsumer';
import changePositionByComment from './changePositionByComment';
import shallowEqual from './shallowEqual';
import getKeepAlive from './getKeepAlive';
export enum COMMAND {
UNACTIVATE = 'unactivate',
UNMOUNT = 'unmount',
ACTIVATE = 'activate',
MOUNT = 'mount',
CURRENT_UNMOUNT = 'current_unmount',
CURRENT_UNACTIVATE = 'current_unactivate',
}
export const keepAliveDisplayName = '$$KeepAlive';
export interface IKeepAliveComponentProps {
interface IListenUpperKeepAliveContainerProps extends IIdentificationContextConsumerComponentProps, IKeepAliveContextConsumerComponentProps {
disabled?: boolean;
}
interface IListenUpperKeepAliveContainerProps extends IKeepAliveComponentProps, IIdentificationContextConsumerComponentProps, IKeepAliveContextConsumerComponentProps {}
interface IListenUpperKeepAliveContainerState {
activated: boolean;
}
......@@ -36,136 +28,20 @@ interface IListenUpperKeepAliveContainerState {
interface ITriggerLifecycleContainerProps extends IKeepAliveContextConsumerComponentProps {
propKey: string;
keepAlive: boolean;
getCombinedKeepAlive: () => boolean;
}
/**
* Decorating the <KeepAlive> component, the main function is to listen to events emitted by the upper <KeepAlive> component, triggering events of the current <KeepAlive> component.
*
* @export
* @template P
* @param {React.ComponentType<any>} Component
* @returns {React.ComponentType<P>}
*/
export default function keepAliveDecorator<P = any>(Component: React.ComponentType<any>): React.ComponentType<P> {
const {
componentDidMount = noop,
componentDidUpdate = noop,
componentWillUnactivate = noop,
componentWillUnmount = noop,
} = Component.prototype;
Component.prototype.componentDidMount = function () {
const {
_container,
} = this.props;
const {
notNeedActivate,
identification,
eventEmitter,
keepAlive,
} = _container;
notNeedActivate();
const cb = () => {
mount.call(this);
listen.call(this);
eventEmitter.off([identification, START_MOUNTING_DOM], cb);
};
eventEmitter.on([identification, START_MOUNTING_DOM], cb);
componentDidMount.call(this);
if (keepAlive) {
this.componentDidActivate();
}
};
Component.prototype.componentDidUpdate = function () {
componentDidUpdate.call(this);
const {
_container,
} = this.props;
const {
notNeedActivate,
isNeedActivate,
} = _container;
if (isNeedActivate()) {
notNeedActivate();
mount.call(this);
listen.call(this);
this._unmounted = false;
this.componentDidActivate();
}
};
Component.prototype.componentWillUnactivate = function () {
componentWillUnactivate.call(this);
unmount.call(this);
unlisten.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);
unlisten.call(this);
}
};
function mount(this: any) {
const {
_container: {
cache,
identification,
storeElement,
setLifecycle,
},
} = this.props;
const {renderElement} = cache[identification];
setLifecycle(LIFECYCLE.UPDATING);
changePositionByComment(identification, renderElement, storeElement);
}
function unmount(this: any) {
const {
_container: {
identification,
storeElement,
cache,
setLifecycle,
},
} = this.props;
const {renderElement, ifStillActivate, reactivate} = cache[identification];
setLifecycle(LIFECYCLE.UNMOUNTED);
changePositionByComment(identification, storeElement, renderElement);
if (ifStillActivate) {
reactivate();
}
}
function listen(this: any) {
const {
_container: {
identification,
eventEmitter,
},
} = this.props;
eventEmitter.on(
[identification, COMMAND.CURRENT_UNMOUNT],
this._bindUnmount = this.componentWillUnmount.bind(this),
);
eventEmitter.on(
[identification, COMMAND.CURRENT_UNACTIVATE],
this._bindUnactivate = this.componentWillUnactivate.bind(this),
);
}
function unlisten(this: any) {
const {
_container: {
identification,
eventEmitter,
},
} = this.props;
eventEmitter.off([identification, COMMAND.CURRENT_UNMOUNT], this._bindUnmount);
eventEmitter.off([identification, COMMAND.CURRENT_UNACTIVATE], this._bindUnactivate);
}
class TriggerLifecycleContainer extends React.PureComponent<ITriggerLifecycleContainerProps> {
private identification: string;
private activated = false;
private ifStillActivate = false;
// Let the lifecycle of the cached component be called normally.
......@@ -186,9 +62,6 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
}
public componentDidMount() {
if (!this.ifStillActivate) {
this.activate();
}
const {
keepAlive,
_keepAliveContextProps: {
......@@ -197,39 +70,18 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
} = this.props;
if (keepAlive) {
this.needActivate = true;
eventEmitter.emit([this.identification, COMMAND.ACTIVATE]);
}
}
public componentDidCatch() {
if (!this.activated) {
this.activate();
eventEmitter.emit([this.identification, COMMAND.MOUNT]);
}
}
public componentWillUnmount() {
const {
getCombinedKeepAlive,
_keepAliveContextProps: {
eventEmitter,
isExisted,
},
} = this.props;
const keepAlive = getCombinedKeepAlive();
if (!keepAlive || !isExisted()) {
eventEmitter.emit([this.identification, COMMAND.CURRENT_UNMOUNT]);
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()) {
eventEmitter.emit([this.identification, COMMAND.CURRENT_UNACTIVATE]);
eventEmitter.emit([this.identification, COMMAND.UNACTIVATE]);
}
}
private activate = () => {
this.activated = true;
eventEmitter.emit([this.identification, COMMAND.CURRENT_UNMOUNT]);
eventEmitter.emit([this.identification, COMMAND.UNMOUNT]);
}
private reactivate = () => {
......@@ -257,7 +109,6 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
const {
propKey,
keepAlive,
getCombinedKeepAlive,
_keepAliveContextProps: {
isExisted,
storeElement,
......@@ -285,7 +136,6 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
const {
isNeedActivate,
notNeedActivate,
activated,
getLifecycle,
setLifecycle,
identification,
......@@ -305,7 +155,6 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
identification,
eventEmitter,
keepAlive,
activated,
getLifecycle,
isExisted,
}}
......@@ -319,7 +168,6 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
eventEmitter,
identification,
storeElement,
keepAlive,
cache,
}}
/>
......@@ -331,15 +179,11 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
}
class ListenUpperKeepAliveContainer extends React.Component<IListenUpperKeepAliveContainerProps, IListenUpperKeepAliveContainerState> {
private combinedKeepAlive: boolean;
public state = {
activated: true,
};
private activate: () => void;
private unactivate: () => void;
private mount: () => void;
private unmount: () => void;
......@@ -383,13 +227,8 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
return;
}
eventEmitter.on(
[identification, COMMAND.ACTIVATE],
this.activate = () => this.setState({activated: true}),
true,
);
eventEmitter.on(
[identification, COMMAND.UNACTIVATE],
this.unactivate = () => this.setState({activated: false}),
[identification, COMMAND.MOUNT],
this.mount = () => this.setState({activated: true}),
true,
);
eventEmitter.on(
......@@ -404,15 +243,10 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
if (!identification) {
return;
}
eventEmitter.off([identification, COMMAND.ACTIVATE], this.activate);
eventEmitter.off([identification, COMMAND.UNACTIVATE], this.unactivate);
eventEmitter.off([identification, COMMAND.MOUNT], this.mount);
eventEmitter.off([identification, COMMAND.UNMOUNT], this.unmount);
}
private getCombinedKeepAlive = () => {
return this.combinedKeepAlive;
}
public render() {
const {
_identificationContextProps: {
......@@ -438,7 +272,7 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
return null;
}
const newKeepAlive = getKeepAlive(propKey, include, exclude, disabled);
this.combinedKeepAlive = getLifecycle === undefined || getLifecycle() === LIFECYCLE.UPDATING
const combinedKeepAlive = getLifecycle === undefined || getLifecycle() === LIFECYCLE.UPDATING
? newKeepAlive
: identification
? upperKeepAlive && newKeepAlive
......@@ -448,8 +282,7 @@ export default function keepAliveDecorator<P = any>(Component: React.ComponentTy
<TriggerLifecycleContainer
{...wrapperProps}
propKey={propKey}
keepAlive={this.combinedKeepAlive}
getCombinedKeepAlive={this.getCombinedKeepAlive}
keepAlive={combinedKeepAlive}
/>
)
: null;
......
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 IdentificationContext from '../contexts/IdentificationContext';
import IdentificationContext, {IIdentificationContextProps} from '../contexts/IdentificationContext';
import getDisplayName from './getDisplayName';
export interface IIdentificationContextProps {
identification: string;
eventEmitter: any;
keepAlive: boolean;
activated: boolean;
getLifecycle: () => number;
isExisted: () => boolean;
}
export interface IIdentificationContextConsumerComponentProps {
_identificationContextProps: IIdentificationContextProps;
}
......
import React from 'react';
import {IKeepAliveProviderImpl, IKeepAliveProviderProps} from '../components/Provider';
import KeepAliveContext from '../contexts/KeepAliveContext';
import KeepAliveContext, {IKeepAliveContextProps} from '../contexts/KeepAliveContext';
import getDisplayName from './getDisplayName';
type IKeepAliveContextProps = IKeepAliveProviderImpl & IKeepAliveProviderProps;
export interface IKeepAliveContextConsumerComponentProps {
_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