├── .editorconfig ├── .github └── workflows │ ├── deploy.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .prettierrc.js ├── .yarn └── releases │ └── yarn-1.19.1.js ├── .yarnrc ├── LICENSE ├── README.md ├── babel.config.js ├── codecov.yml ├── commitlint.config.js ├── examples ├── cra │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── pages │ │ ├── about │ │ │ └── index.js │ │ ├── demo1 │ │ │ ├── CustomSwitch │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── demo2 │ │ │ ├── CustomRoutes │ │ │ │ └── index.js │ │ │ ├── StillnessOutlets │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── demo3 │ │ │ └── index.js │ │ ├── demo4 │ │ │ ├── CountHooks.js │ │ │ └── index.js │ │ ├── demo5 │ │ │ └── index.js │ │ ├── detail │ │ │ └── index.js │ │ └── list │ │ │ ├── count │ │ │ ├── countClass.js │ │ │ ├── countHooks.js │ │ │ └── index.js │ │ │ └── index.js │ │ ├── reportWebVitals.js │ │ └── setupTests.js ├── cra18 │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ └── setupTests.js ├── next │ ├── .babelrc │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── components │ │ └── StillnessSwitch.jsx │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.js │ │ ├── index.js │ │ ├── one.js │ │ ├── three.js │ │ └── two.js │ ├── public │ │ ├── favicon.ico │ │ └── vercel.svg │ └── styles │ │ ├── Home.module.css │ │ └── globals.css ├── umi │ ├── .editorconfig │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── .umirc.ts │ ├── README.md │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── Count │ │ │ │ ├── countClass.tsx │ │ │ │ └── index.tsx │ │ │ └── StillnessComponent │ │ │ │ └── index.tsx │ │ ├── layouts │ │ │ └── index.tsx │ │ ├── module │ │ │ ├── Conditional.tsx │ │ │ ├── StillnessComponent.tsx │ │ │ ├── StillnessContext.ts │ │ │ ├── StillnessScope.tsx │ │ │ ├── index.ts │ │ │ └── withStillnessHoc.tsx │ │ └── pages │ │ │ ├── a.tsx │ │ │ ├── about.tsx │ │ │ ├── home.tsx │ │ │ └── list.tsx │ ├── tsconfig.json │ └── typings.d.ts └── vite │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── src │ ├── App.css │ ├── App.tsx │ ├── favicon.svg │ ├── index.css │ ├── logo.svg │ ├── main.tsx │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── jest └── setup-testing-library.js ├── lerna.json ├── package.json ├── packages ├── react-stillness │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest-setup.ts │ ├── jest-transformer.js │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── Offscreen.tsx │ │ │ ├── __tests__ │ │ │ │ └── Offscreen.spec.tsx │ │ │ └── index.tsx │ │ ├── constants.ts │ │ ├── core │ │ │ ├── StillnessContext.ts │ │ │ ├── StillnessNodeContext.ts │ │ │ ├── StillnessProvider.tsx │ │ │ ├── __tests__ │ │ │ │ ├── StillnessProvider.spec.tsx │ │ │ │ └── StillnessStore.spec.tsx │ │ │ ├── actions │ │ │ │ ├── index.ts │ │ │ │ ├── maxActions.ts │ │ │ │ ├── operationAction.ts │ │ │ │ └── vNodeAction.ts │ │ │ ├── classes │ │ │ │ ├── StillnessManagerImpl.ts │ │ │ │ ├── StillnessMonitorImpl.ts │ │ │ │ └── index.ts │ │ │ ├── createStillnessManager.ts │ │ │ ├── index.ts │ │ │ └── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── max.ts │ │ │ │ ├── operation.ts │ │ │ │ └── vNode.ts │ │ ├── decorators │ │ │ ├── __tests__ │ │ │ │ ├── connectStillness.spec.tsx │ │ │ │ ├── decorateHandler.spec.tsx │ │ │ │ └── withNodeBridge.spec.tsx │ │ │ ├── connectStillness.tsx │ │ │ ├── decorateHandler.tsx │ │ │ ├── index.tsx │ │ │ └── withNodeBridge.tsx │ │ ├── hooks │ │ │ ├── HandleImpl.ts │ │ │ ├── __tests__ │ │ │ │ ├── HandleImpl.spec.ts │ │ │ │ ├── useOptionalFactory.spec.ts │ │ │ │ ├── useStillness.spec.tsx │ │ │ │ └── useStillnessManager.spec.tsx │ │ │ ├── index.tsx │ │ │ ├── useCollectedProps.ts │ │ │ ├── useCollector.ts │ │ │ ├── useIsomorphicLayoutEffect.ts │ │ │ ├── useOptionalFactory.ts │ │ │ ├── useReceive.ts │ │ │ ├── useStillness.ts │ │ │ ├── useStillnessContract.ts │ │ │ ├── useStillnessHandle.ts │ │ │ ├── useStillnessManager.ts │ │ │ └── useStillnessMonitor.ts │ │ ├── index.ts │ │ ├── internals │ │ │ ├── StillnessContractImpl.ts │ │ │ ├── StillnessHandleImpl.ts │ │ │ ├── StillnessRegistrationImpl.ts │ │ │ ├── __tests__ │ │ │ │ ├── StillnessContractImpl.spec.tsx │ │ │ │ ├── StillnessHandleImpl.spec.ts │ │ │ │ └── stillnessRegistrationImpl.spec.ts │ │ │ └── index.tsx │ │ ├── types │ │ │ ├── contract.ts │ │ │ ├── core.ts │ │ │ ├── decorators.ts │ │ │ ├── handle.ts │ │ │ ├── hooks.ts │ │ │ ├── index.ts │ │ │ ├── monitors.ts │ │ │ └── registration.ts │ │ └── utils │ │ │ ├── combineFuncs.ts │ │ │ ├── dom.ts │ │ │ ├── getNextUniqueId.ts │ │ │ ├── getNodes.ts │ │ │ ├── index.ts │ │ │ ├── is.ts │ │ │ ├── shallowEqual.ts │ │ │ └── throttle.ts │ └── tsconfig.json ├── umi-plugin-stillness │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── assets │ │ │ └── exports.tpl │ │ └── index.ts │ └── tsconfig.json └── umi-renderer-stillness │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ ├── index.d.ts │ ├── index.ts │ ├── renderClient │ │ └── renderClient.tsx │ └── renderRoutes │ │ ├── Route.tsx │ │ ├── StillnessSwitch.tsx │ │ └── renderRoutes.tsx │ ├── tsconfig.json │ └── types.d.ts ├── rollup.config.js ├── scripts └── release.sh ├── tsconfig.base.json ├── website ├── .gitignore ├── README.md ├── babel.config.js ├── blog │ ├── BDD Test.md │ ├── assets │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.gif │ │ ├── 6.gif │ │ ├── 7.gif │ │ ├── 8.gif │ │ └── 9.gif │ ├── authors.yml │ ├── react中如何实现keep-alive.md │ └── 用TDD的思想做组件测试.md ├── docs │ ├── api │ │ ├── Components │ │ │ ├── Offscreen.md │ │ │ ├── StillnessProvider.md │ │ │ └── _category_.json │ │ ├── Decorators │ │ │ ├── _category_.json │ │ │ └── connectStillness.md │ │ ├── Hooks │ │ │ ├── _category_.json │ │ │ ├── useStillness.md │ │ │ └── useStillnessManager.md │ │ ├── contract-state.md │ │ └── global.md │ ├── basic-concepts │ │ ├── contract.md │ │ ├── items-types.md │ │ └── max.md │ ├── examples │ │ ├── index.md │ │ ├── nextjs │ │ │ ├── _category_.json │ │ │ └── index.md │ │ ├── react-router │ │ │ ├── _category_.json │ │ │ ├── v5.md │ │ │ └── v6.md │ │ ├── simple │ │ │ ├── _category_.json │ │ │ └── intro.md │ │ └── umi │ │ │ ├── _category_.json │ │ │ └── v3.md │ ├── faq.md │ ├── get-started.md │ └── intro.md ├── docusaurus.config.js ├── i18n │ └── zh-CN │ │ ├── code.json │ │ ├── docusaurus-plugin-content-blog │ │ ├── assets │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.gif │ │ │ ├── 6.gif │ │ │ ├── 7.gif │ │ │ ├── 8.gif │ │ │ └── 9.gif │ │ ├── authors.yml │ │ ├── options.json │ │ ├── react中如何实现keep-alive.md │ │ └── 用TDD的思想做组件测试.md │ │ ├── docusaurus-plugin-content-docs │ │ ├── current.json │ │ └── current │ │ │ ├── api │ │ │ ├── Components │ │ │ │ ├── Offscreen.md │ │ │ │ ├── StillnessProvider.md │ │ │ │ └── _category_.json │ │ │ ├── Decorators │ │ │ │ ├── _category_.json │ │ │ │ └── connectStillness.md │ │ │ ├── Hooks │ │ │ │ ├── _category_.json │ │ │ │ ├── useStillness.md │ │ │ │ └── useStillnessManager.md │ │ │ ├── contract-state.md │ │ │ └── global.md │ │ │ ├── basic-concepts │ │ │ ├── contract.md │ │ │ ├── items-types.md │ │ │ └── max.md │ │ │ ├── examples │ │ │ ├── index.md │ │ │ ├── nextjs │ │ │ │ ├── _category_.json │ │ │ │ └── index.md │ │ │ ├── react-router │ │ │ │ ├── _category_.json │ │ │ │ ├── v5.md │ │ │ │ └── v6.md │ │ │ ├── simple │ │ │ │ ├── _category_.json │ │ │ │ └── intro.md │ │ │ └── umi │ │ │ │ ├── _category_.json │ │ │ │ └── v3.md │ │ │ ├── faq.md │ │ │ ├── get-started.md │ │ │ └── intro.md │ │ └── docusaurus-theme-classic │ │ ├── footer.json │ │ └── navbar.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── HomepageEditor │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ └── HomepageFeatures │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ ├── pages │ │ ├── index.module.css │ │ └── index.tsx │ └── theme │ │ ├── CodeBlock │ │ ├── index.js │ │ └── styles.module.css │ │ └── DocPage │ │ ├── index.js │ │ └── styles.module.css ├── static │ ├── .nojekyll │ ├── baidu_verify_code-7U9vS3Ps0V.html │ └── img │ │ ├── code1.png │ │ ├── favicon.ico │ │ ├── intro.gif │ │ ├── logo.png │ │ ├── real-dom.png │ │ ├── tutorial │ │ ├── docsVersionDropdown.png │ │ └── localeDropdown.png │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg ├── tsconfig.json └── yarn.lock └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/deploy.yml' 9 | - 'website/**' 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-20.04 14 | concurrency: 15 | group: ${{ github.workflow }}-${{ github.ref }} 16 | defaults: 17 | run: 18 | working-directory: website 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Setup Node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: '14' 26 | 27 | - name: Get yarn cache 28 | id: yarn-cache 29 | run: echo "::set-output name=dir::$(yarn cache dir)" 30 | 31 | - name: Cache dependencies 32 | uses: actions/cache@v2 33 | with: 34 | path: ${{ steps.yarn-cache.outputs.dir }} 35 | key: ${{ runner.os }}-website-${{ hashFiles('**/yarn.lock') }} 36 | restore-keys: | 37 | ${{ runner.os }}-website- 38 | 39 | - run: yarn install 40 | - run: yarn build:gh 41 | 42 | - name: Deploy 43 | uses: peaceiris/actions-gh-pages@v3 44 | if: ${{ github.ref == 'refs/heads/main' }} 45 | with: 46 | github_token: ${{ secrets.GITHUB_TOKEN }} 47 | publish_dir: ./website/build -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | publish-gpr: 13 | name: Publish to GPR 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 14.19.1 24 | 25 | - name: Config git user 26 | run: | 27 | git config --global user.name "${{ github.actor }}" 28 | git config --global user.email "${{ github.actor }}@outlook.com" 29 | 30 | - name: Install Dependencies 31 | run: yarn install 32 | 33 | - name: Bump versions packages 34 | run: yarn ci 35 | env: 36 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | 38 | - name: Publish coverage to codecov.io 39 | uses: codecov/codecov-action@v3 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | npminstall-debug.log* 9 | package-lock.json* 10 | 11 | # Test Artifacts 12 | coverage/ 13 | 14 | # production 15 | /build 16 | /dll 17 | **/dist 18 | **/es 19 | /lib -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn run lint:staged 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth:2, 3 | semi:true, 4 | singleQuote: true, 5 | bracketSpacing:true, 6 | requirePragma:false, 7 | }; 8 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | yarn-path ".yarn/releases/yarn-1.19.1.js" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 leomYili 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://img.shields.io/npm/v/react-stillness-component?color=%23007ec6&style=flat-square)](https://www.npmjs.com/package/react-stillness-component) [![package size](https://img.shields.io/bundlephobia/min/react-stillness-component?style=flat-square)](https://bundlephobia.com/result?p=react-stillness-component@latest) [![LICENSE](https://img.shields.io/npm/l/react-stillness-component.svg?style=flat-square)](https://github.com/leomYili/react-stillness-component/blob/main/LICENSE)[![codecov](https://codecov.io/gh/leomYili/react-stillness-component/branch/main/graph/badge.svg?token=CX32T2S9YK)](https://codecov.io/gh/leomYili/react-stillness-component) 2 | 3 |

4 | 5 | 6 | 7 |

8 | 9 | # react-stillness-component 10 | 11 | like vue keep-alive 12 | 13 | [react-router-v5 demo](https://codesandbox.io/s/example-react-routerv5-iily7i?file=/src/Example.js:332-343) 14 | 15 | See the docs, tutorials and examples on the website: 16 | 17 | 18 | 19 | or 20 | 21 | [react-stillness-component.vercel.app](https://react-stillness-component.vercel.app/) 22 | 23 | See the changelog on the Releases page: 24 | 25 | 26 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | esmodules: true, 9 | }, 10 | bugfixes: true, 11 | loose: true, 12 | }, 13 | ], 14 | '@babel/preset-typescript', 15 | [ 16 | '@babel/preset-react', 17 | { 18 | runtime: 'automatic', 19 | }, 20 | ], 21 | ], 22 | plugins: [ 23 | ['@babel/plugin-proposal-class-properties', { loose: true }], 24 | '@babel/plugin-transform-react-jsx', 25 | ['@babel/plugin-proposal-private-methods', { loose: true }], 26 | ['@babel/plugin-proposal-private-property-in-object', { loose: true }], 27 | '@babel/plugin-proposal-object-rest-spread', 28 | '@babel/plugin-transform-runtime', 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: '70...100' 8 | status: 9 | patch: off 10 | 11 | parsers: 12 | gcov: 13 | branch_detection: 14 | conditional: yes 15 | loop: yes 16 | method: no 17 | macro: no 18 | 19 | comment: 20 | layout: 'reach,diff,flags,tree' 21 | behavior: default 22 | require_changes: no 23 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | } -------------------------------------------------------------------------------- /examples/cra/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /examples/cra/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/cra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cra-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-router-dom": "^5.3.0", 12 | "react-router-domv6": "npm:react-router-dom", 13 | "react-scripts": "4.0.3", 14 | "react-stillness-component": "^0.9.0", 15 | "web-vitals": "^1.0.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject", 22 | "clean": "rimraf dist" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "webpack": "4.44.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/cra/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/cra/public/favicon.ico -------------------------------------------------------------------------------- /examples/cra/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/cra/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/cra/public/logo192.png -------------------------------------------------------------------------------- /examples/cra/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/cra/public/logo512.png -------------------------------------------------------------------------------- /examples/cra/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/cra/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/cra/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40px; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | justify-content: center; 22 | font-size: calc(10px + 2vmin); 23 | color: white; 24 | } 25 | 26 | .App-link { 27 | color: #61dafb; 28 | } 29 | 30 | @keyframes App-logo-spin { 31 | from { 32 | transform: rotate(0deg); 33 | } 34 | to { 35 | transform: rotate(360deg); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/cra/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | iframe { 16 | pointer-events: none; 17 | } -------------------------------------------------------------------------------- /examples/cra/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | // import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want to start measuring performance in your app, pass a function 10 | // to log results (for example: reportWebVitals(console.log)) 11 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 12 | reportWebVitals(); 13 | -------------------------------------------------------------------------------- /examples/cra/src/pages/about/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | 3 | const About = () => { 4 | return
这是关于页,展示面包屑控件
; 5 | }; 6 | 7 | export default About; 8 | -------------------------------------------------------------------------------- /examples/cra/src/pages/demo1/CustomSwitch/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { withRouter, matchPath } from 'react-router-dom'; 3 | import { Offscreen } from 'react-stillness-component'; 4 | 5 | const StillnessSwitch = (props) => { 6 | const { match, location, childLocation } = props; 7 | const [paths, setPaths] = useState([]); 8 | 9 | const _location = childLocation || location; 10 | 11 | let element, isExist; 12 | 13 | return React.Children.map(props.children, (child) => { 14 | if (React.isValidElement(child)) { 15 | const path = child.props.path || child.props.from; 16 | 17 | const childMatch = isExist 18 | ? null 19 | : path 20 | ? matchPath(_location.pathname, { ...child.props, path }) 21 | : match; 22 | 23 | if (childMatch !== null) { 24 | isExist = true; 25 | } 26 | 27 | element = ( 28 | 29 | {React.cloneElement(child, { 30 | _location, 31 | computedMatch: childMatch, 32 | })} 33 | 34 | ); 35 | 36 | return element; 37 | } 38 | }); 39 | }; 40 | 41 | export default withRouter(StillnessSwitch); 42 | -------------------------------------------------------------------------------- /examples/cra/src/pages/demo1/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import { BrowserRouter, Switch, Route, Link } from 'react-router-dom'; 4 | 5 | import { StillnessProvider, Offscreen } from 'react-stillness-component'; 6 | 7 | import { CountHooks } from '../list/count/countHooks'; 8 | 9 | import CustomSwitch from './CustomSwitch'; 10 | 11 | export default function demo1({ match, location }) { 12 | return ( 13 | <> 14 |
15 |

16 | 17 | 去one 18 | 19 |

20 |

21 | 22 | 去two 23 | 24 |

25 |
26 |
27 | { 29 | // return ; 30 | 31 | return ( 32 | 33 | { 37 | console.log(props); 38 | return ; 39 | }} 40 | > 41 | { 45 | return
two
; 46 | }} 47 | /> 48 |
49 | ); 50 | }} 51 | /> 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /examples/cra/src/pages/demo2/StillnessOutlets/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useEffect } from 'react'; 2 | 3 | import { Offscreen } from 'react-stillness-component'; 4 | import { useOutlet, useLocation } from 'react-router-domv6'; 5 | 6 | export const StillnessOutLets = () => { 7 | const [outlets, setOutlets] = useState([]); 8 | let location = useLocation(); 9 | const outlet = useOutlet(); 10 | let locationPathname = location.pathname; 11 | let locationKey = location.key; 12 | 13 | useEffect(() => { 14 | const result = outlets.some((o) => { 15 | if (o.pathname === locationPathname) { 16 | return true; 17 | } 18 | }); 19 | 20 | if (!result) { 21 | setOutlets([ 22 | ...outlets, 23 | { 24 | key: locationKey, 25 | pathname: locationPathname, 26 | outlet, 27 | }, 28 | ]); 29 | } 30 | }, [locationPathname]); 31 | 32 | return ( 33 | <> 34 | 测试文字 35 | {outlets.map((o, i) => { 36 | return ( 37 | 38 | {o.outlet} 39 | 40 | ); 41 | })} 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /examples/cra/src/pages/demo2/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import { BrowserRouter, Routes, Route, Link } from 'react-router-domv6'; 4 | 5 | import { Offscreen } from 'react-stillness-component'; 6 | 7 | import { StillnessRoutes } from './CustomRoutes'; 8 | import { StillnessOutLets } from './StillnessOutlets'; 9 | import { WithCount } from '../list/count/countClass'; 10 | 11 | export default function demo2({ match, location }) { 12 | return ( 13 | 14 |
15 |

16 | 17 | 去one 18 | 19 |

20 |

21 | 22 | 去two 23 | 24 |

25 |
26 |
27 | 28 | 32 | 33 | 34 | } 35 | > 36 | } /> 37 | two} /> 38 | 39 | 40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /examples/cra/src/pages/demo4/CountHooks.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useEffect, useState } from 'react'; 2 | import { useStillness } from 'react-stillness-component'; 3 | 4 | export function CountHooks(props) { 5 | const [count, setCount] = useState(0); 6 | 7 | const collected = useStillness(() => { 8 | return { 9 | mounted: (contract) => { 10 | console.log('开始进入静止状态', props); 11 | return 'mounted'; 12 | }, 13 | unmounted: (contract) => { 14 | console.log('退出静止状态', props); 15 | 16 | return 'unmounted'; 17 | }, 18 | collect: (contract) => { 19 | return { 20 | isStillness: contract.isStillness(), 21 | stillnessId: contract.getStillnessId(), 22 | item: contract.getStillnessItem(), 23 | }; 24 | }, 25 | }; 26 | }); 27 | 28 | useEffect(() => { 29 | // console.log(collected); 30 | }, [collected]); 31 | 32 | return ( 33 |
34 |

hook count: {count}

35 |

parent count: {props.parentCount}

36 | 43 | {props.children} 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /examples/cra/src/pages/demo4/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import { CountHooks } from './CountHooks'; 4 | 5 | import { Offscreen } from 'react-stillness-component'; 6 | 7 | export default function Demo3({ match, location }) { 8 | const [visible, setVisible] = useState(true); 9 | const [count, setCount] = useState(0); 10 | 11 | return ( 12 |
17 | 18 | 19 | 20 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /examples/cra/src/pages/detail/index.js: -------------------------------------------------------------------------------- 1 | import React, { useReducer } from 'react'; 2 | 3 | const initialState = { 4 | loading: false, 5 | count: 0, 6 | }; 7 | 8 | const reducer = (state, action) => { 9 | 10 | console.log(action); 11 | switch (action.type) { 12 | case 'increment': { 13 | return { ...state, count: state.count + 1, loading: false }; 14 | } 15 | case 'decrement': { 16 | return { ...state, count: state.count - 1, loading: false }; 17 | } 18 | case 'loading': { 19 | return { ...state, loading: true }; 20 | } 21 | default: { 22 | return state; 23 | } 24 | } 25 | }; 26 | 27 | const delay = (time = 1500) => { 28 | return new Promise(resolve => { 29 | setTimeout(() => { 30 | resolve(true); 31 | }, time); 32 | }); 33 | }; 34 | 35 | function PokemonInfo() { 36 | const [{ count, loading }, dispatch] = useReducer(reducer, initialState); 37 | const onHandleIncrement = () => { 38 | dispatch({ type: 'increment' }); 39 | dispatch({ type: 'increment' }); 40 | dispatch({ type: 'increment' }); 41 | }; 42 | const onHandleDecrement = () => { 43 | dispatch({ type: 'loading' }); 44 | delay(500); 45 | dispatch({ type: 'decrement' }); 46 | }; 47 | return ( 48 |
49 |

Count {loading ? 'loading..' : count}

50 | 53 | 56 |
57 | ); 58 | } 59 | 60 | export default PokemonInfo; -------------------------------------------------------------------------------- /examples/cra/src/pages/list/count/countClass.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connectStillness } from 'react-stillness-component'; 3 | 4 | export class Count extends Component { 5 | state = { 6 | count: 0, 7 | }; 8 | 9 | componentDidMount(){ 10 | console.log("初始化"); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 |

count: {this.state.count}

17 | 24 | {this.props.children} 25 |
26 | ); 27 | } 28 | } 29 | 30 | const spec = { 31 | mounted: (props, contract) => { 32 | // console.log('开始进入静止状态', contract.getStillnessId()); 33 | 34 | // return 'mounted'; 35 | }, 36 | unmounted: (props, contract) => { 37 | // console.log('退出静止状态'); 38 | 39 | // return 'unmounted'; 40 | }, 41 | collect: (props, contract) => { 42 | return { 43 | isStillness: contract.isStillness(), 44 | stillnessId: contract.getStillnessId(), 45 | }; 46 | }, 47 | }; 48 | 49 | export const WithCount = connectStillness(spec)(Count); 50 | -------------------------------------------------------------------------------- /examples/cra/src/pages/list/count/countHooks.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useEffect, useState } from 'react'; 2 | import { useStillness } from 'react-stillness-component'; 3 | 4 | const spec = { 5 | mounted: (contract) => { 6 | console.log('开始进入静止状态', contract.getStillnessId()); 7 | 8 | return 'mounted'; 9 | }, 10 | unmounted: (contract) => { 11 | console.log('退出静止状态'); 12 | 13 | return 'unmounted'; 14 | }, 15 | collect: (contract) => { 16 | return { 17 | isStillness: contract.isStillness(), 18 | stillnessId: contract.getStillnessId(), 19 | item: contract.getStillnessItem(), 20 | }; 21 | }, 22 | }; 23 | 24 | export function CountHooks(props) { 25 | const [count, setCount] = useState(0); 26 | const collected = useStillness(spec); 27 | 28 | useEffect(() => { 29 | console.log(collected); 30 | }, [collected]); 31 | 32 | return ( 33 |
34 |

hook count: {count}

35 | 42 | {props.children} 43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /examples/cra/src/pages/list/count/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useRef, useEffect } from 'react'; 2 | 3 | import { Offscreen } from 'react-stillness-component'; 4 | 5 | export const Count = (props) => { 6 | const [count, setCount] = useState(0); 7 | 8 | const offscreen1Ref = useRef(null); 9 | 10 | /* useEffect(() => { 11 | console.log(offscreen1Ref.current?.isStillness()); 12 | }); */ 13 | 14 | return ( 15 |
16 |

function count: {count}

17 | 25 |
32 | 33 |
测试嵌套结构
34 |
35 |
36 |
37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /examples/cra/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /examples/cra/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /examples/cra18/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/cra18/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cra18", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^13.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-scripts": "5.0.1", 12 | "react-stillness-component": "^0.8.4", 13 | "web-vitals": "^2.1.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "clean": "rimraf dist" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/cra18/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/cra18/public/favicon.ico -------------------------------------------------------------------------------- /examples/cra18/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/cra18/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/cra18/public/logo192.png -------------------------------------------------------------------------------- /examples/cra18/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/cra18/public/logo512.png -------------------------------------------------------------------------------- /examples/cra18/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/cra18/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/cra18/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/cra18/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { 3 | StillnessProvider, 4 | Offscreen, 5 | useStillness, 6 | } from 'react-stillness-component'; 7 | import logo from './logo.svg'; 8 | import './App.css'; 9 | 10 | function Content() { 11 | const [show, setShow] = useState(true); 12 | 13 | return ( 14 |
15 | 16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | 23 | function Count() { 24 | const [count, setCount] = useState(0); 25 | const collected = useStillness({ 26 | mounted: (contract) => { 27 | return 'mounted'; 28 | }, 29 | unmounted: (contract) => { 30 | return 'unmounted'; 31 | }, 32 | collect: (contract) => { 33 | return { 34 | isStillness: contract.isStillness(), 35 | stillnessId: contract.getStillnessId(), 36 | }; 37 | }, 38 | }); 39 | 40 | return ( 41 |
42 | stillness:{collected.isStillness.toString()} 43 |

class count: {count}

44 | 51 |
52 | ); 53 | } 54 | 55 | function App() { 56 | return ( 57 | 58 |
59 |
60 | logo 61 |

62 | Edit src/App.js and save to reload. 63 |

64 | 70 | Learn React 71 | 72 |
73 | 74 |
75 |
76 | ); 77 | } 78 | 79 | export default App; 80 | -------------------------------------------------------------------------------- /examples/cra18/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/cra18/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/cra18/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /examples/cra18/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /examples/cra18/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /examples/next/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [] 4 | } -------------------------------------------------------------------------------- /examples/next/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/babel","next/core-web-vitals"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/next/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /examples/next/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /examples/next/components/StillnessSwitch.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useRouter } from 'next/router'; 3 | import { Offscreen } from 'react-stillness-component'; 4 | 5 | function matchPath(pathname, routes) { 6 | const result = routes.find(({ path }) => path === pathname) || null; 7 | 8 | return result; 9 | } 10 | 11 | const StillnessSwitch = (props) => { 12 | const { Component, pageProps } = props; 13 | const router = useRouter(); 14 | const [stillnessRoutes, setStillnessRoutes] = useState([]); 15 | const [route, setRoute] = useState([]); 16 | 17 | useEffect(() => { 18 | if (pageProps.stillness) { 19 | !matchPath(router.pathname, stillnessRoutes) && 20 | setStillnessRoutes([ 21 | ...stillnessRoutes, 22 | { Page: Component, _props: pageProps, path: router.pathname }, 23 | ]); 24 | setRoute([]); 25 | } else { 26 | setRoute([ 27 | { 28 | Page: Component, 29 | _props: pageProps, 30 | path: router.pathname, 31 | }, 32 | ]); 33 | } 34 | }, [Component, router.pathname]); 35 | 36 | return ( 37 | <> 38 | {stillnessRoutes.concat(route).map(({ Page, _props, path }) => { 39 | if (_props.stillness) { 40 | return ( 41 | 46 | 47 | 48 | ); 49 | } 50 | 51 | return ; 52 | })} 53 | 54 | ); 55 | }; 56 | 57 | export default StillnessSwitch; 58 | -------------------------------------------------------------------------------- /examples/next/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /examples/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "clean": "rimraf dist" 11 | }, 12 | "dependencies": { 13 | "next": "12.1.4", 14 | "react": "17.0.2", 15 | "react-dom": "17.0.2", 16 | "react-stillness-component": "^0.8.1" 17 | }, 18 | "devDependencies": { 19 | "eslint": "8.20.0", 20 | "@types/react": "17.0.39", 21 | "eslint-config-next": "12.2.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/next/pages/_app.js: -------------------------------------------------------------------------------- 1 | import { StillnessProvider } from 'react-stillness-component'; 2 | import '../styles/globals.css'; 3 | 4 | import StillnessSwitch from '../components/StillnessSwitch'; 5 | 6 | function MyApp({ Component, pageProps }) { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default MyApp; 15 | -------------------------------------------------------------------------------- /examples/next/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Image from 'next/image'; 3 | import Link from 'next/link'; 4 | import styles from '../styles/Home.module.css'; 5 | 6 | export default function Home() { 7 | return ( 8 |
9 | 10 | Create Next App 11 | 12 | 13 | 14 | 15 |
16 |

17 | Welcome to Next.js! 18 |

19 | 20 |
    21 |
  • 22 | 23 | 去one 24 | 25 |
  • 26 |
  • 27 | 28 | 去two 29 | 30 |
  • 31 |
  • 32 | 33 | 去three 34 | 35 |
  • 36 |
37 |
38 |
39 | 40 | 52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /examples/next/pages/one.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Offscreen } from 'react-stillness-component'; 3 | 4 | function One() { 5 | const [count, setCount] = React.useState(0); 6 | 7 | return ( 8 | 9 |
10 | 会静止的页面{count} 11 | 18 |
19 |
20 | ); 21 | } 22 | 23 | export async function getStaticProps(context) { 24 | return { 25 | props: { 26 | stillness: true, 27 | }, 28 | }; 29 | } 30 | 31 | export default One; 32 | -------------------------------------------------------------------------------- /examples/next/pages/three.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Three() { 4 | return ( 5 |
6 |
7 | 这是测试文字,一直延长, 这是测试文字,一直延长 这是测试文字,一直延长 8 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 9 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 10 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 11 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 12 |
13 |
14 | ); 15 | } 16 | 17 | export async function getStaticProps(context) { 18 | return { 19 | props: { 20 | stillness: true, 21 | }, 22 | }; 23 | } 24 | 25 | export default Three; -------------------------------------------------------------------------------- /examples/next/pages/two.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Two() { 4 | const [count, setCount] = React.useState(0); 5 | 6 | return ( 7 |
8 | 不静止的页面{count} 9 | 16 |
17 | ); 18 | } 19 | 20 | export default Two; 21 | -------------------------------------------------------------------------------- /examples/next/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/examples/next/public/favicon.ico -------------------------------------------------------------------------------- /examples/next/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /examples/next/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /examples/umi/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /examples/umi/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | 16 | # umi 17 | /src/.umi 18 | /src/.umi-production 19 | /src/.umi-test 20 | /.env.local 21 | -------------------------------------------------------------------------------- /examples/umi/.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.html 5 | package.json 6 | .umi 7 | .umi-production 8 | .umi-test 9 | -------------------------------------------------------------------------------- /examples/umi/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 80, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /examples/umi/.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'umi'; 2 | 3 | export default defineConfig({ 4 | nodeModulesTransform: { 5 | type: 'none', 6 | }, 7 | routes: [ 8 | { 9 | exact: false, 10 | path: '/', 11 | component: '@/layouts/index', 12 | routes: [ 13 | { 14 | exact: false, 15 | path: '/home', 16 | component: '@/pages/home', 17 | stillness: true, 18 | routes: [ 19 | { 20 | path: '/home/a', 21 | component: '@/pages/a', 22 | stillness: true, 23 | }, 24 | ], 25 | }, 26 | { path: '/about', component: '@/pages/about', stillness: true }, 27 | { path: '/list', component: '@/pages/list' }, 28 | ], 29 | }, 30 | ], 31 | stillness: {}, 32 | }); 33 | -------------------------------------------------------------------------------- /examples/umi/README.md: -------------------------------------------------------------------------------- 1 | # umi project 2 | 3 | ## Getting Started 4 | 5 | Install dependencies, 6 | 7 | ```bash 8 | $ yarn 9 | ``` 10 | 11 | Start the dev server, 12 | 13 | ```bash 14 | $ yarn start 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/umi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "umi-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "umi dev", 7 | "build": "umi build", 8 | "postinstall": "umi generate tmp", 9 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", 10 | "test": "umi-test", 11 | "test:coverage": "umi-test --coverage", 12 | "clean": "rimraf dist" 13 | }, 14 | "gitHooks": { 15 | "pre-commit": "lint-staged" 16 | }, 17 | "lint-staged": { 18 | "*.{js,jsx,less,md,json}": [ 19 | "prettier --write" 20 | ], 21 | "*.ts?(x)": [ 22 | "prettier --parser=typescript --write" 23 | ] 24 | }, 25 | "dependencies": { 26 | "@ant-design/pro-layout": "^6.5.0", 27 | "react": "17.x", 28 | "react-dom": "17.x", 29 | "react-router-dom": "5.2.0", 30 | "react-stillness-component": "0.8.3", 31 | "umi": "^3.5.26", 32 | "umi-plugin-stillness": "0.2.5" 33 | }, 34 | "devDependencies": { 35 | "@types/react": "^17.0.0", 36 | "@types/react-dom": "^17.0.0", 37 | "@umijs/preset-react": "1.x", 38 | "@umijs/test": "^3.5.20", 39 | "lint-staged": "^10.0.7", 40 | "prettier": "^2.2.0", 41 | "typescript": "^4.1.2", 42 | "yorkie": "^2.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/umi/src/components/Count/countClass.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connectStillness } from 'umi'; 3 | 4 | interface Props { 5 | isStillness: any; 6 | stillnessId: any; 7 | } 8 | 9 | export class Count extends Component { 10 | state = { 11 | count: 0, 12 | }; 13 | 14 | render() { 15 | return ( 16 |
17 |

count: {this.state.count}

18 | 25 | {this.props.children} 26 |
27 | ); 28 | } 29 | } 30 | 31 | const spec = { 32 | mounted: (props, contract) => { 33 | console.log('开始进入静止状态', contract.getStillnessId()); 34 | 35 | // return 'mounted'; 36 | }, 37 | unmounted: (props, contract) => { 38 | console.log('退出静止状态'); 39 | 40 | // return 'unmounted'; 41 | }, 42 | collect: (props, contract) => { 43 | return { 44 | isStillness: contract.isStillness(), 45 | stillnessId: contract.getStillnessId(), 46 | }; 47 | }, 48 | }; 49 | 50 | export default connectStillness(spec)(Count); 51 | -------------------------------------------------------------------------------- /examples/umi/src/components/Count/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useStillness, useStillnessManager } from 'umi'; 3 | 4 | export default function Count() { 5 | const [count, setCount] = useState(0); 6 | const stillnessManager = useStillnessManager(); 7 | const collected = useStillness({ 8 | mounted: (contract) => { 9 | console.log('被隐藏'); 10 | return 'mounted'; 11 | }, 12 | unmounted: (contract) => { 13 | return 'unmounted'; 14 | }, 15 | collect: (contract) => { 16 | return { 17 | isStillness: contract.isStillness(), 18 | stillnessId: contract.getStillnessId(), 19 | }; 20 | }, 21 | }); 22 | 23 | console.log(collected.isStillness); 24 | 25 | return ( 26 |
27 |

class count: {count}

28 | 35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /examples/umi/src/components/StillnessComponent/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class StillnessComponent extends Component { 4 | constructor(props:any){ 5 | super(props); 6 | 7 | 8 | } 9 | } 10 | 11 | export default StillnessComponent; 12 | -------------------------------------------------------------------------------- /examples/umi/src/layouts/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { IRouteComponentProps, Router,StillnessProvider } from 'umi'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | export default function Layout({ 6 | children, 7 | location, 8 | route, 9 | history, 10 | match, 11 | }: IRouteComponentProps) { 12 | return ( 13 | <> 14 |
15 | 测试 16 | home 17 |
{ 19 | history.push('/home/a'); 20 | }} 21 | > 22 | Go to home child page a 23 |
24 |
{ 26 | history.push('/list'); 27 | }} 28 | > 29 | Go to list page 30 |
31 |
{ 33 | history.push('/about'); 34 | }} 35 | > 36 | Go to about page 37 |
38 |
39 | {children} 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /examples/umi/src/module/Conditional.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useRef, useLayoutEffect } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | export const Conditional = (props: any) => { 5 | const [targetElement] = useState(() => document.createElement('div')); 6 | const containerRef: any = useRef(undefined); 7 | 8 | const activatedRef = useRef(false); 9 | activatedRef.current = activatedRef.current || props.active; 10 | 11 | useLayoutEffect(() => { 12 | if (props.active) { 13 | containerRef?.current?.appendChild(targetElement); 14 | } else { 15 | try { 16 | containerRef?.current?.removeChild(targetElement); 17 | } catch (e) {} 18 | } 19 | }, [props.active]); 20 | return ( 21 | <> 22 |
23 | {activatedRef?.current && 24 | ReactDOM.createPortal(props.children, targetElement)} 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /examples/umi/src/module/StillnessContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const StillnessContext = createContext(null); 4 | export const { Provider, Consumer } = StillnessContext; 5 | -------------------------------------------------------------------------------- /examples/umi/src/module/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StillnessScope'; 2 | export { default as WithStillness } from './withStillnessHoc'; 3 | 4 | -------------------------------------------------------------------------------- /examples/umi/src/module/withStillnessHoc.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import { Consumer } from './StillnessContext'; 4 | import { StillnessComponent } from './StillnessComponent'; 5 | 6 | const withStillnessHoc = (WrappedComponent: any) => { 7 | return class extends Component { 8 | private extraDom: any = null; 9 | private myRef: React.RefObject; 10 | 11 | constructor(props: any) { 12 | super(props); 13 | 14 | this.myRef = React.createRef(); 15 | } 16 | 17 | componentDidMount() { 18 | /* let extraDom = document.getElementById('extraDom'); 19 | 20 | if (!extraDom) { 21 | extraDom = document.createElement('div'); 22 | extraDom.id = 'extraDom'; 23 | document.body.appendChild(extraDom); 24 | } 25 | 26 | this.extraDom = extraDom; */ 27 | } 28 | 29 | componentWillUnmount() { 30 | // console.log(this.props.children); 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | {(context) => { 37 | return ( 38 | 43 | ); 44 | }} 45 | 46 | ); 47 | } 48 | }; 49 | }; 50 | 51 | export default withStillnessHoc(StillnessComponent); 52 | -------------------------------------------------------------------------------- /examples/umi/src/pages/a.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Offscreen } from 'umi'; 3 | import Count from '../components/Count'; 4 | 5 | export default function a() { 6 | return ( 7 | <> 8 |
9 |

a

10 | 11 | 12 |
13 | 14 |
15 |
16 | 这是测试文字,一直延长, 这是测试文字,一直延长 这是测试文字,一直延长 17 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 18 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 19 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 20 | 这是测试文字,一直延长 这是测试文字,一直延长 这是测试文字,一直延长 21 |
22 |
23 |
24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /examples/umi/src/pages/about.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WithCount from '../components/Count/countClass'; 3 | 4 | export default function About() { 5 | return ( 6 |
7 |

About

8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/umi/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { Link, useStillnessManager } from 'umi'; 3 | 4 | function Home(props: any) { 5 | const manager = useStillnessManager(); 6 | return ( 7 |
8 |

Home

9 | {/* Go to home page 10 | Go to list page 11 | Go to about page */} 12 | 19 |
20 | {props.children} 21 |
22 | ); 23 | } 24 | 25 | export default Home; 26 | -------------------------------------------------------------------------------- /examples/umi/src/pages/list.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { Link, Route } from 'react-router-dom'; 3 | 4 | const getData = () => { 5 | let data = []; 6 | for (let i = 0; i < 200; i++) { 7 | data.push({ key: i, value: i }); 8 | } 9 | return data; 10 | }; 11 | 12 | export default function List({ match }: any) { 13 | return ( 14 | <> 15 |

list(scroll)

16 |
    17 | {getData().map((v, i) => { 18 | return
  • {v.value}
  • ; 19 | })} 20 |
21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /examples/umi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "resolveJsonModule": true, 7 | "importHelpers": true, 8 | "jsx": "react-jsx", 9 | "esModuleInterop": true, 10 | "sourceMap": true, 11 | "baseUrl": "./", 12 | "strict": true, 13 | "paths": { 14 | "@/*": ["src/*"], 15 | "@@/*": ["src/.umi/*"] 16 | } 17 | }, 18 | "include": [ 19 | "mock/**/*", 20 | "src/**/*", 21 | "config/**/*", 22 | ".umirc.ts", 23 | "typings.d.ts" 24 | ], 25 | "exclude": [ 26 | "node_modules", 27 | "lib", 28 | "es", 29 | "dist", 30 | "typings", 31 | "**/__test__", 32 | "test", 33 | "docs", 34 | "tests" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /examples/umi/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.less'; 3 | declare module '*.png'; 4 | declare module '*.svg' { 5 | export function ReactComponent( 6 | props: React.SVGProps, 7 | ): React.ReactElement; 8 | const url: string; 9 | export default url; 10 | } 11 | -------------------------------------------------------------------------------- /examples/vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "preview": "vite preview", 9 | "clean": "rimraf dist" 10 | }, 11 | "dependencies": { 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-stillness-component": "0.8.3" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.2", 18 | "@types/react-dom": "^17.0.2", 19 | "@vitejs/plugin-react": "^1.3.0", 20 | "typescript": "^4.6.3", 21 | "vite": "^3.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/vite/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /examples/vite/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { StillnessProvider, Offscreen } from 'react-stillness-component'; 3 | 4 | import logo from './logo.svg'; 5 | import './App.css'; 6 | 7 | function App() { 8 | const [count, setCount] = useState(0); 9 | const [visible, setVisible] = useState(false); 10 | 11 | console.log(visible); 12 | 13 | return ( 14 | 15 |
16 |
17 | logo 18 |

Hello Vite + React!

19 | 20 |

21 | 27 |

28 |
29 |

30 | Edit App.tsx and save to test HMR updates. 31 |

32 | 39 |

40 | 46 | Learn React 47 | 48 | {' | '} 49 | 55 | Vite Docs 56 | 57 |

58 |
59 |
60 |
61 | ); 62 | } 63 | 64 | export default App; 65 | -------------------------------------------------------------------------------- /examples/vite/src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/vite/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/vite/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /examples/vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /examples/vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /examples/vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /jest/setup-testing-library.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*"], 3 | "command": { 4 | "version": { 5 | "allowBranch": ["main"], 6 | "ignoreChanges": ["**/*.md"] 7 | }, 8 | "publish": { 9 | "registry": "https://registry.npmjs.org" 10 | } 11 | }, 12 | "changelog": { 13 | "repo": "react-stillness-component", 14 | "labels": { 15 | "pr(feature)": ":feat: New Feature", 16 | "pr(enhancement)": ":rocket: Enhancement", 17 | "pr(bug)": ":bug: Bug Fix", 18 | "pr(documentation)": ":book: Documentation", 19 | "pr(dependency)": ":deciduous_tree: Dependency", 20 | "pr(chore)": ":turtle: Chore" 21 | }, 22 | "cacheDir": ".changelog" 23 | }, 24 | "ignoreChanges": [ 25 | "**/*.md", 26 | "**/*.test.ts", 27 | "**/*.e2e.ts", 28 | "**/fixtures/**", 29 | "**/test/**" 30 | ], 31 | "npmClient": "yarn", 32 | "useWorkspaces": false, 33 | "version": "independent" 34 | } 35 | -------------------------------------------------------------------------------- /packages/react-stillness/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | tsconfig.json 3 | *.tsbuildinfo -------------------------------------------------------------------------------- /packages/react-stillness/jest-setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; -------------------------------------------------------------------------------- /packages/react-stillness/jest-transformer.js: -------------------------------------------------------------------------------- 1 | const babelJest = require("babel-jest"); 2 | 3 | module.exports = babelJest.createTransformer({ 4 | presets: [ 5 | [ 6 | '@babel/preset-env', 7 | { 8 | targets: { 9 | node: 'current', 10 | esmodules: true, 11 | }, 12 | bugfixes: true, 13 | loose: true, 14 | }, 15 | ], 16 | '@babel/preset-typescript', 17 | ], 18 | plugins: [ 19 | ['@babel/plugin-proposal-class-properties', { loose: true }], 20 | '@babel/plugin-transform-react-jsx', 21 | ['@babel/plugin-proposal-private-methods', { loose: true }], 22 | ['@babel/plugin-proposal-private-property-in-object', { loose: true }], 23 | '@babel/plugin-proposal-object-rest-spread', 24 | '@babel/plugin-transform-runtime', 25 | ], 26 | }); -------------------------------------------------------------------------------- /packages/react-stillness/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupFilesAfterEnv: ['./jest-setup.ts'], 3 | testMatch: ["**/__tests__/**/?(*.)(spec|test).[jt]s?(x)"], 4 | // testRegex: 'decorateHandler.spec.tsx', 5 | transform: { 6 | "\\.[jt]sx?$": "./jest-transformer.js", 7 | }, 8 | collectCoverageFrom: [ 9 | '**/src/**/*.tsx', 10 | '**/src/**/*.ts', 11 | '!**/__tests__/**', 12 | '!**/dist/**', 13 | ], 14 | globals: { 15 | __DEV__: true, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/react-stillness/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-stillness-component", 3 | "version": "0.9.1", 4 | "description": "This package makes react-component static, with all dom operations disabled and invisible, and supports hook as well as class-component", 5 | "bugs": { 6 | "url": "https://github.com/leomYili/react-stillness-component/issues" 7 | }, 8 | "homepage": "https://github.com/leomYili/react-stillness-component#readme", 9 | "main": "dist/index.js", 10 | "module": "es/index.ts", 11 | "scripts": { 12 | "clean": "rimraf dist", 13 | "test": "jest" 14 | }, 15 | "keywords": [ 16 | "react-component", 17 | "keep-alive", 18 | "component" 19 | ], 20 | "author": "leomyili", 21 | "license": "MIT", 22 | "publishConfig": { 23 | "main": "dist/index.js", 24 | "module": "es/index.ts", 25 | "types": "dist/src/index.d.ts" 26 | }, 27 | "files": [ 28 | "dist", 29 | "es" 30 | ], 31 | "types": "dist/src/index.d.ts", 32 | "dependencies": { 33 | "fast-deep-equal": "^3.1.3", 34 | "hoist-non-react-statics": "^3.3.2", 35 | "invariant": "^2.2.4", 36 | "redux": "^4.1.2" 37 | }, 38 | "devDependencies": { 39 | "@types/jest": "^27.5.0", 40 | "@types/react": "^17.x", 41 | "@types/react-dom": "^17.x", 42 | "react": "^17.x", 43 | "react-dom": "^17.x" 44 | }, 45 | "peerDependencies": { 46 | "@types/hoist-non-react-statics": ">= 3.3.1", 47 | "@types/node": ">= 12", 48 | "@types/react": ">= 16.8", 49 | "react": "^16.8 || ^17.0 || ^18.0", 50 | "react-dom": "^16.8 || ^17.0 || ^18.0" 51 | }, 52 | "rollup": { 53 | "input": "src/index.ts", 54 | "output": [ 55 | { 56 | "file": "dist/index.js", 57 | "format": "umd", 58 | "name": "ReactStillness", 59 | "globals": { 60 | "react": "React", 61 | "react-dom": "ReactDOM" 62 | } 63 | }, 64 | { 65 | "file": "es/index.ts", 66 | "format": "esm", 67 | "name": "ReactStillness", 68 | "globals": { 69 | "react": "React", 70 | "react-dom": "ReactDOM" 71 | } 72 | } 73 | ], 74 | "external": [ 75 | "react", 76 | "react-dom" 77 | ] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /packages/react-stillness/src/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { 2 | default as Offscreen, 3 | OffscreenProps, 4 | OffscreenInnerProps, 5 | } from './Offscreen'; 6 | -------------------------------------------------------------------------------- /packages/react-stillness/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const operationTypes = { 2 | MOUNT: 'MOUNT', 3 | UNMOUNT: 'UNMOUNT', 4 | UNSET: 'UNSET', 5 | CLEAR: 'CLEAR', 6 | }; 7 | 8 | export const NONE = 'NONE'; 9 | 10 | export const lifeCycleTypes = [operationTypes.MOUNT, operationTypes.UNMOUNT]; 11 | 12 | export const effectTypes = [operationTypes.UNSET, operationTypes.CLEAR]; 13 | 14 | export const rootId = '__root__'; 15 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/StillnessContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | import { StillnessContextType } from '../types'; 4 | 5 | export const StillnessContext = createContext({ 6 | stillnessManager: undefined, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/StillnessNodeContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | import { StillnessNodeContextType } from '../types'; 4 | 5 | export const StillnessNodeContext = createContext({ 6 | stillnessParentId: undefined, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/StillnessProvider.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reset */ 2 | 3 | import React, { FC, useEffect, memo, useReducer, useMemo } from 'react'; 4 | import { StillnessProviderProps, StillnessManager } from '../types'; 5 | import { StillnessContext } from './StillnessContext'; 6 | import { 7 | createStillnessManager, 8 | getGlobalContext, 9 | INSTANCE_SYM, 10 | } from './createStillnessManager'; 11 | 12 | let refCount = 0; 13 | 14 | /** 15 | * stillness Components 的上下文,用于缓存所有静止实例 16 | * @param param0 17 | * @returns 18 | */ 19 | export const StillnessProvider: FC> = memo( 20 | function ({ children, ...props }) { 21 | const [stillnessManager, isGlobalInstance] = 22 | useStillnessContextValue(props); 23 | 24 | /* 25 | * 保持全局的实例统一 26 | * 并且保持退出即销毁实例,释放内存 27 | */ 28 | useEffect(() => { 29 | if (isGlobalInstance) { 30 | const context = getGlobalContext(); 31 | ++refCount; 32 | 33 | return () => { 34 | if (--refCount === 0) { 35 | context[INSTANCE_SYM] = null; 36 | } 37 | }; 38 | } 39 | }, []); 40 | 41 | return ( 42 | 43 | {({ stillnessManager: parentStillnessManager }) => { 44 | if (parentStillnessManager) { 45 | console.warn( 46 | 'There is already a stillnessManager, please check if there is an error, there may be unknown problems' 47 | ); 48 | } 49 | 50 | return ( 51 | 52 | {children} 53 | 54 | ); 55 | }} 56 | 57 | ); 58 | } 59 | ); 60 | 61 | function useStillnessContextValue(props: StillnessProviderProps): any { 62 | return useMemo(() => { 63 | const manager = createStillnessManager( 64 | props.context, 65 | props.options, 66 | props.debugMode 67 | ); 68 | 69 | const isGlobalInstance = !props.context; 70 | 71 | return [manager, isGlobalInstance]; 72 | }, [props]); 73 | } 74 | 75 | declare const global: any; 76 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/actions/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createVNode, 3 | removeVNode, 4 | updateVNode, 5 | resetVNode, 6 | } from './vNodeAction'; 7 | import { 8 | triggerMount, 9 | triggerUnmount, 10 | triggerUnset, 11 | triggerClear, 12 | } from './operationAction'; 13 | import { resetMax, createCache, updateCache, removeCache } from './maxActions'; 14 | import { StillnessManager } from '../../types'; 15 | import { combineFuncs } from '../../utils'; 16 | 17 | export function createVNodeActions(manager: StillnessManager) { 18 | const actions = combineFuncs( 19 | { 20 | createVNode, 21 | removeVNode, 22 | updateVNode, 23 | resetVNode, 24 | }, 25 | manager 26 | ); 27 | 28 | return actions; 29 | } 30 | 31 | export function createOperationActions(manager: StillnessManager) { 32 | const actions = combineFuncs( 33 | { 34 | triggerMount, 35 | triggerUnmount, 36 | triggerUnset, 37 | triggerClear, 38 | }, 39 | manager 40 | ); 41 | 42 | return actions; 43 | } 44 | 45 | export function createMaxActions(manager: StillnessManager) { 46 | const actions = combineFuncs( 47 | { 48 | resetMax, 49 | createCache, 50 | updateCache, 51 | removeCache, 52 | }, 53 | manager 54 | ); 55 | 56 | return actions; 57 | } 58 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/actions/maxActions.ts: -------------------------------------------------------------------------------- 1 | import { Action, StillnessManager, MaxPayload } from '../../types'; 2 | import { operationTypes, rootId, NONE } from '../../constants'; 3 | import { getNodeIdsByCondition } from '../../utils/getNodes'; 4 | 5 | export const RESET_MAX = 'stillness/resetMax'; 6 | export const ADD_CACHE = 'stillness/addCache'; 7 | export const UPDATE_CACHE = 'stillness/updateCache'; 8 | export const REMOVE_CACHE = 'stillness/removeCache'; 9 | 10 | export function resetMax(manager: StillnessManager) { 11 | return (payload: MaxPayload): Action => { 12 | return { 13 | type: RESET_MAX, 14 | payload: { 15 | max: payload.max, 16 | }, 17 | }; 18 | }; 19 | } 20 | 21 | export function createCache(manager: StillnessManager) { 22 | return (payload: MaxPayload): Action => { 23 | return { 24 | type: ADD_CACHE, 25 | payload: payload, 26 | }; 27 | }; 28 | } 29 | 30 | export function updateCache(manager: StillnessManager) { 31 | return function updateVNode(payload: MaxPayload): Action { 32 | return { 33 | type: UPDATE_CACHE, 34 | payload: payload, 35 | }; 36 | }; 37 | } 38 | 39 | export function removeCache(manager: StillnessManager) { 40 | return (payload: MaxPayload): Action => { 41 | return { 42 | type: REMOVE_CACHE, 43 | payload: payload, 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/actions/vNodeAction.ts: -------------------------------------------------------------------------------- 1 | import { Action, VNodePayload, StillnessManager } from '../../types'; 2 | 3 | export const ADD_V_NODE = 'stillness/addVNode'; 4 | export const REMOVE_V_NODE = 'stillness/removeVNode'; 5 | export const RESET_V_NODE = 'stillness/resetVNode'; 6 | export const UPDATE_V_NODE = 'stillness/updateVNode'; 7 | 8 | export function createVNode(manager: StillnessManager) { 9 | return (payload: VNodePayload): Action => { 10 | return { 11 | type: ADD_V_NODE, 12 | payload: payload, 13 | }; 14 | }; 15 | } 16 | 17 | export function updateVNode(manager: StillnessManager) { 18 | return function updateVNode(payload: VNodePayload): Action { 19 | return { 20 | type: UPDATE_V_NODE, 21 | payload: payload, 22 | }; 23 | }; 24 | } 25 | 26 | export function removeVNode(manager: StillnessManager) { 27 | return (payload: VNodePayload): Action => { 28 | return { 29 | type: REMOVE_V_NODE, 30 | payload: payload, 31 | }; 32 | }; 33 | } 34 | 35 | export function resetVNode(manager: StillnessManager) { 36 | return (payload: VNodePayload): Action => { 37 | return { 38 | type: RESET_V_NODE, 39 | payload: payload, 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/classes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StillnessManagerImpl'; 2 | export * from './StillnessMonitorImpl'; -------------------------------------------------------------------------------- /packages/react-stillness/src/core/createStillnessManager.ts: -------------------------------------------------------------------------------- 1 | import { createStore, Store } from 'redux'; 2 | 3 | import { stillnessReducers, State } from './reducers'; 4 | import { StillnessManagerImpl, StillnessMonitorImpl } from './classes'; 5 | import { StillnessManager, ProviderOptions } from '../types'; 6 | import { isUndefined } from '../utils'; 7 | 8 | function makeStoreInstance(debugMode: boolean): Store { 9 | // TODO: if we ever make a react-native version of this, 10 | // we'll need to consider how to pull off dev-tooling 11 | const reduxDevTools = 12 | typeof window !== 'undefined' && 13 | (window as any).__REDUX_DEVTOOLS_EXTENSION__; 14 | return createStore( 15 | stillnessReducers, 16 | debugMode && 17 | reduxDevTools && 18 | reduxDevTools({ 19 | name: 'react-stillness', 20 | instanceId: 'react-stillness', 21 | }) 22 | ); 23 | } 24 | 25 | export const INSTANCE_SYM = '__REACT_STILLNESS_CONTEXT_INSTANCE__'; 26 | 27 | export function getGlobalContext() { 28 | return typeof global !== 'undefined' ? global : (window as any); 29 | } 30 | 31 | export function createStillnessManager( 32 | globalContext: unknown = getGlobalContext(), 33 | options: ProviderOptions = { max: -1 }, 34 | debugMode: boolean = false 35 | ): StillnessManager { 36 | const ctx = globalContext as any; 37 | 38 | if (!ctx[INSTANCE_SYM]) { 39 | const store = makeStoreInstance(debugMode); 40 | const monitor = new StillnessMonitorImpl(store); 41 | const manager = new StillnessManagerImpl(store, monitor, options); 42 | 43 | ctx[INSTANCE_SYM] = { 44 | stillnessManager: manager, 45 | }; 46 | } 47 | 48 | if (!isUndefined(options) && options?.max >= 0) { 49 | // 进行初始化,重置store 50 | ctx[INSTANCE_SYM].stillnessManager 51 | .getActions() 52 | .resetMax({ max: options.max }); 53 | } 54 | 55 | return ctx[INSTANCE_SYM].stillnessManager; 56 | } 57 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StillnessContext'; 2 | export * from './StillnessProvider'; 3 | export * from './StillnessNodeContext'; 4 | export { INSTANCE_SYM } from './createStillnessManager'; 5 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { UniqueId, Action } from '../../types'; 2 | import { reduce as vNode, State as vNodeState } from './vNode'; 3 | import { reduce as max, State as maxState } from './max'; 4 | import { reduce as operation, State as Operation } from './operation'; 5 | 6 | export interface State { 7 | /** 8 | * Record all stationary component base data 9 | */ 10 | vNodes: { [key: UniqueId]: vNodeState }; 11 | /** 12 | * Maximum number of components that can be stillness 13 | */ 14 | max: maxState; 15 | operation: Operation; 16 | } 17 | 18 | export function stillnessReducers( 19 | state: State = {} as State, 20 | action: Action 21 | ): State { 22 | return { 23 | vNodes: vNode(state.vNodes, action), 24 | max: max(state.max, action), 25 | operation: operation(state.operation, action), 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/reducers/max.ts: -------------------------------------------------------------------------------- 1 | import { Action, UniqueId, MaxPayload } from '../../types'; 2 | import { 3 | RESET_MAX, 4 | ADD_CACHE, 5 | UPDATE_CACHE, 6 | REMOVE_CACHE, 7 | } from '../actions/maxActions'; 8 | 9 | export interface State { 10 | capacity: number | boolean; 11 | caches: UniqueId[]; 12 | } 13 | 14 | const initialState: State = { 15 | capacity: -1, 16 | caches: [], 17 | }; 18 | 19 | export function reduce( 20 | state: State = initialState, 21 | action: Action<{ 22 | max?: number | boolean; 23 | cacheId: UniqueId; 24 | }> 25 | ): State { 26 | const { payload } = action; 27 | 28 | switch (action.type) { 29 | case RESET_MAX: 30 | if (payload.max) { 31 | state.capacity = payload.max; 32 | } 33 | 34 | return { ...state }; 35 | case ADD_CACHE: 36 | if (payload.cacheId) { 37 | state.caches.push(payload.cacheId); 38 | } 39 | return { ...state }; 40 | case UPDATE_CACHE: 41 | if (state.caches.length) { 42 | const index = state.caches.indexOf(payload.cacheId); 43 | if (index > -1) { 44 | state.caches.splice(index, 1); 45 | } 46 | 47 | state.caches.push(payload.cacheId); 48 | } 49 | 50 | return { ...state }; 51 | case REMOVE_CACHE: 52 | state.caches.shift(); 53 | return { ...state }; 54 | default: 55 | return state; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/reducers/operation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TRIGGER_MOUNT, 3 | TRIGGER_UNMOUNT, 4 | TRIGGER_CLEAR, 5 | TRIGGER_UNSET, 6 | } from '../actions/operationAction'; 7 | import { Action, UniqueId, OperationTypes } from '../../types'; 8 | import { NONE, operationTypes } from '../../constants'; 9 | import { getNowTimeStr } from '../../utils'; 10 | 11 | export interface State { 12 | type: OperationTypes; 13 | targetIds: UniqueId[]; 14 | targetType: string | null; 15 | now: string; 16 | } 17 | 18 | const initialState: State = { 19 | type: null, 20 | targetIds: [], 21 | targetType: null, 22 | now: getNowTimeStr(), 23 | }; 24 | 25 | export function reduce( 26 | state: State = initialState, 27 | action: Action 28 | ): State { 29 | switch (action.type) { 30 | case TRIGGER_MOUNT: 31 | return { 32 | ...state, 33 | type: operationTypes.MOUNT as OperationTypes, 34 | targetIds: action.payload.targetIds, 35 | now: getNowTimeStr(), 36 | }; 37 | case TRIGGER_UNMOUNT: 38 | return { 39 | ...state, 40 | type: operationTypes.UNMOUNT as OperationTypes, 41 | targetIds: action.payload.targetIds, 42 | now: getNowTimeStr(), 43 | }; 44 | case TRIGGER_UNSET: 45 | return { 46 | ...state, 47 | type: operationTypes.UNSET as OperationTypes, 48 | targetIds: action.payload.targetIds, 49 | targetType: action.payload.targetType, 50 | now: getNowTimeStr(), 51 | }; 52 | case TRIGGER_CLEAR: 53 | return { 54 | ...state, 55 | type: operationTypes.CLEAR as OperationTypes, 56 | targetIds: action.payload.targetIds, 57 | targetType: null, 58 | now: getNowTimeStr(), 59 | }; 60 | case NONE: 61 | default: 62 | return state; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/react-stillness/src/core/reducers/vNode.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_V_NODE, 3 | REMOVE_V_NODE, 4 | RESET_V_NODE, 5 | UPDATE_V_NODE, 6 | } from '../actions/vNodeAction'; 7 | import {} from '../../utils'; 8 | import { UniqueId, Action, Identifier } from '../../types'; 9 | 10 | export interface State { 11 | uniqueId: UniqueId; 12 | type?: UniqueId; 13 | parentId: UniqueId; 14 | visible?: boolean; 15 | isStillness?: boolean; 16 | childQueue?: UniqueId[]; 17 | } 18 | 19 | export function reduce( 20 | state: { [key: UniqueId]: State } = {}, 21 | action: Action<{ 22 | uniqueId?: UniqueId; 23 | parentId: UniqueId; 24 | type?: Identifier; 25 | visible?: boolean; 26 | isStillness?: boolean; 27 | childQueue?: UniqueId[]; 28 | }> 29 | ): { [key: string]: State } { 30 | const { payload } = action; 31 | 32 | switch (action.type) { 33 | case ADD_V_NODE: 34 | if (payload.uniqueId) { 35 | state[payload.uniqueId] = { 36 | ...payload, 37 | uniqueId: payload.uniqueId, 38 | }; 39 | } 40 | return { ...state }; 41 | case UPDATE_V_NODE: 42 | if (payload.uniqueId && state[payload.uniqueId]) { 43 | state[payload.uniqueId] = { 44 | ...state[payload.uniqueId], 45 | ...payload, 46 | uniqueId: payload.uniqueId, 47 | }; 48 | } 49 | return { ...state }; 50 | case RESET_V_NODE: 51 | /* if (!state[payload.id]) { 52 | state[payload.id] = { 53 | ...state[payload.oldId], 54 | ...rest, 55 | }; 56 | 57 | if (payload.oldId !== payload.id) { 58 | delete state[payload.oldId]; 59 | } 60 | } */ 61 | return { ...state }; 62 | case REMOVE_V_NODE: 63 | if (payload.uniqueId) { 64 | delete state[payload.uniqueId]; 65 | } 66 | 67 | return { ...state }; 68 | default: 69 | return state; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/react-stillness/src/decorators/__tests__/connectStillness.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import { jest } from '@jest/globals'; 4 | 5 | import { StillnessProvider } from '../../core/StillnessProvider'; 6 | import { connectStillness } from '../connectStillness'; 7 | import { Offscreen } from '../../components'; 8 | 9 | describe('connectStillness', () => { 10 | it('Will an error be reported when there are no parameters', () => { 11 | console.error = jest.fn(); 12 | class Component extends React.Component {} 13 | 14 | expect(() => { 15 | // @ts-ignore 16 | connectStillness()(Component); 17 | }).toThrow(/spec is required/i); 18 | }); 19 | 20 | it('Will an error be reported when there is no collect', () => { 21 | console.warn = jest.fn(); 22 | class Component extends React.Component { 23 | render() { 24 | return
; 25 | } 26 | } 27 | 28 | const DecoratedClass = connectStillness({})(Component); 29 | 30 | render( 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | 38 | expect(global.console.warn).toBeCalled(); 39 | }); 40 | 41 | it('show null when there is no context', () => { 42 | class Component extends React.Component { 43 | render() { 44 | return
test
; 45 | } 46 | } 47 | 48 | const DecoratedClass = connectStillness({})(Component); 49 | 50 | const { queryByTestId } = render(); 51 | 52 | expect(queryByTestId('test')).not.toBeInTheDocument(); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/react-stillness/src/decorators/__tests__/decorateHandler.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import { jest } from '@jest/globals'; 4 | import userEvent from '@testing-library/user-event'; 5 | 6 | import { StillnessProvider } from '../../core/StillnessProvider'; 7 | import { connectStillness } from '../connectStillness'; 8 | import { Offscreen } from '../../components'; 9 | import { debug } from 'console'; 10 | 11 | describe('decorateHandler', () => { 12 | it('', async () => { 13 | class Component extends React.Component { 14 | render() { 15 | return
{this.props.item}
; 16 | } 17 | } 18 | 19 | const spec = { 20 | unmounted: () => { 21 | return 'unmounted'; 22 | }, 23 | collect: (props: any, contract: any) => { 24 | return { 25 | item: contract.getStillnessItem(), 26 | }; 27 | }, 28 | }; 29 | 30 | const DecoratedClass = connectStillness(spec as any)(Component); 31 | 32 | const Demo = () => { 33 | const [visible, setVisible] = React.useState(true); 34 | 35 | return ( 36 | 37 | 38 | 39 | 40 | 41 | 42 | 20 | // highlight-start 21 | 22 | 23 | 24 | // highlight-end 25 |
26 | ); 27 | } 28 | ``` 29 | 30 | ## Props 31 | 32 | - `visible`: Required, **boolean**, used to manually control whether the component is quiescent or not, will start quiescent component only when `true`, if initialized with `false`, it will automatically turn on **lazy loading** 33 | - `type`: optional, **string** or **number**, help you categorize the type of static components, easy to manage and control manually 34 | - `scrollReset`: optional, **boolean**, by default, scroll position will be saved automatically, if you don't need this feature, please set to `false`. -------------------------------------------------------------------------------- /website/docs/api/Components/StillnessProvider.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | id: stillnessProvider 4 | title: StillnessProvider 5 | --- 6 | 7 | > The `StillnessProvider` is the core of the stillness component and needs to be placed outside of all `` components. 8 | 9 | ## Usage 10 | 11 | ```jsx 12 | import React from 'react'; 13 | import ReactDOM from 'react-dom'; 14 | import { StillnessProvider } from 'react-stillness-component'; 15 | import App from './App'; 16 | 17 | ReactDOM.render( 18 | 19 | 20 | , 21 | document.getElementById('root') 22 | ); 23 | ``` 24 | 25 | ## Props 26 | 27 | - `context`: optional, used to configure the current static context, defaults to `window` 28 | - `options`: optional, global configuration parameter, default is `{}` 29 | - `max`: optional, the maximum value of the quiescent state, defaults to `-1` 30 | - `debugMode`: optional, whether to enable debug mode, default is `false` -------------------------------------------------------------------------------- /website/docs/api/Components/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":0 3 | } -------------------------------------------------------------------------------- /website/docs/api/Decorators/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":1 3 | } -------------------------------------------------------------------------------- /website/docs/api/Hooks/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":2 3 | } -------------------------------------------------------------------------------- /website/docs/api/Hooks/useStillness.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | id: useStillness 4 | title: useStillness 5 | --- 6 | 7 | > `useStillness` connects the React component to the stillness-component control center, which provides the connected component with data from the control center and the functions it can use to manipulate the still, and you can declare the properties and methods you want 8 | 9 | ## usage 10 | 11 | ```jsx 12 | import { useStillness } from 'react-stillness-component'; 13 | 14 | function Count(props) { 15 | const [count, setCount] = useState(0); 16 | const collected = useStillness({ 17 | mounted: (contract) => { 18 | return 'mounted'; 19 | }, 20 | unmounted: (contract) => { 21 | return 'unmounted'; 22 | }, 23 | collect: (contract) => { 24 | return { 25 | isStillness: contract.isStillness(), 26 | stillnessId: contract.getStillnessId(), 27 | item: contract.getStillnessItem(), 28 | }; 29 | }, 30 | }); 31 | 32 | useEffect(() => { 33 | console.log(collected); 34 | }, [collected]); 35 | 36 | return
...
; 37 | } 38 | ``` 39 | 40 | ## Parameters 41 | 42 | - `mounted`: optional, **function**, used to describe the initialization behavior of a stationary component with the following parameters 43 | - `props`: optional, **object**, the props of the component 44 | - `contract`: Required, **object**, the contract of the component, see [Contract State](api/contract-state.md) for more information 45 | - `unmounted`: optional, **function**, used to describe the unmounting behavior of a stillness-component with the following arguments 46 | - `props`: optional, **object**, the props of the component 47 | - `contract`: Required, **object**, the contract of the component, see [Contract State](api/contract-state.md) for more information 48 | - `collect`: optional, **function**, used to describe the data collection behavior of a stillness-component with the following parameters 49 | - `props`: optional, **object**, the props of the component 50 | - `contract`: optional, **object**, the contract of the component, see [Contract State](api/contract-state.md) for more information 51 | -------------------------------------------------------------------------------- /website/docs/api/Hooks/useStillnessManager.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | id: useStillnessManager 4 | title: useStillnessManager 5 | --- 6 | 7 | > `useStillnessManager` provides access to control the stillness system, and the returned instance object contains access to all stillness states, contracts, etc. 8 | 9 | ## usage 10 | 11 | ```jsx 12 | import { useStillnessManager } from 'react-stillness-component'; 13 | 14 | function Example() { 15 | const stillnessManager = useStillnessManager(); 16 | 17 | return
Example
18 | } 19 | ``` 20 | 21 | ## Instance(object) 22 | 23 | - `getStore`: Get the abstract global modeling object-store 24 | - `getMonitor`: Get the internal listener methods 25 | - `getActions`: Get all internal behaviors 26 | - `dispatch`: call the **dispatch** method of `redux` 27 | -------------------------------------------------------------------------------- /website/docs/api/contract-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | id: contract-state 4 | title: Contract State 5 | --- 6 | 7 | > `Contract State` is the argument returned to the `collect` function, which will provide various information about a particular stillness-component (typically the `` of the most recent hierarchy). 8 | 9 | ## Methods 10 | 11 | - `getStillnessId()`: returns a **string**, which is the unique identifier of the current stillness component 12 | - `getStillnessItem()`: returns **object**, which is the data of the current stillness component 13 | - `getStillnessType()`: returns a **string**, which is the type of the current stillness component 14 | - `isStillness()`: returns a **boolean**, For checking the current stationary state 15 | - `unset(id?: string,type?: string)`: does not return a value, but specifies the unique identifier and type of the still component to be unloaded, and can also be seen as a refresh component 16 | - `clear()`: no return value, clear the state of all stillness-components, can also be seen as refreshing ones. 17 | - `resetMax(max: number)`: For resetting the maximum value -------------------------------------------------------------------------------- /website/docs/api/global.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | id: global 4 | title: global 5 | --- 6 | 7 | The component now exposes the global property - `INSTANCE_SYM` which is mounted on the current global variable (e.g. `window`) 8 | 9 | It can also be referenced directly via the string - `__REACT_STILLNESS_CONTEXT_INSTANCE__`, in order to support more powerful customization capabilities in special scenarios, without having to obtain `manager` via hooks or contexts -------------------------------------------------------------------------------- /website/docs/basic-concepts/contract.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: contract 3 | title: Contract 4 | --- 5 | 6 | > `contract` means a public convention for changes to a stillness-component that will provide various information about the particular stillness-component. It responds to stillness state changes by updating the `props` of the wrapper component. 7 | 8 | ## collect 9 | 10 | For each component that needs to listen to the stillness state, a pure function can be customized and passed to `connectStillness` or `useStillness`. The stillness-component will collect and call this function when the stillness state is modified and merge the return value into `props`. 11 | 12 | The example is as follows: 13 | 14 | ```jsx title="Hoc" 15 | const spec = { 16 | // highlight-start 17 | collect: (props,contract) => ({ 18 | isStillness: contract.isStillness(), 19 | stillnessId: contract.getStillnessId(), 20 | }); 21 | // highlight-end 22 | }; 23 | 24 | connectStillness(spec); 25 | ``` 26 | 27 | or 28 | 29 | ```jsx title="Hooks" 30 | const spec = (props) => { 31 | return { 32 | // highlight-start 33 | collect: (contract) => ({ 34 | isStillness: contract.isStillness(), 35 | stillnessId: contract.getStillnessId(), 36 | }) 37 | // highlight-end 38 | } 39 | } 40 | 41 | function Count(props) { 42 | const collected = useStillness(spec(props)); 43 | ... 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /website/docs/basic-concepts/items-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: items-types 3 | title: Items and Types 4 | --- 5 | 6 | > The use of `Items` and `Types` is often necessary when listening to the various states of a stillness-component. 7 | 8 | ## Items 9 | 10 | `Items` are **JavaScript objects** that describe the content being staged. When starting a stillness-component, you can create an object like `{type: "counted", data:{...}}` object. This object is then passed to the component's `props` for use. 11 | 12 | ```jsx 13 | ... 14 | const spec = { 15 | mounted: (props, contract) => { 16 | return { 17 | type: 'mounted', 18 | data: { 19 | ... 20 | } 21 | }; 22 | }, 23 | unmounted: (props, contract) => { 24 | return { 25 | type: 'unmounted', 26 | data: { 27 | ... 28 | } 29 | }; 30 | }, 31 | collect: (props, contract) => { 32 | return { 33 | isStillness: contract.isStillness(), 34 | stillnessId: contract.getStillnessId(), 35 | }; 36 | }, 37 | }; 38 | 39 | connectStillness(spec) or useStillness(spec); 40 | ... 41 | ``` 42 | 43 | :::info Info 44 | 45 | `Items` are not required to be returned, you can choose to return anything you want 46 | 47 | ::: 48 | 49 | ## Types 50 | 51 | `Type` is a `string` that identifies a stillness-component of the same class in the application. Typically represented as `type="card"` in applications, `Type` is not required and can be passed via `props` when you have external control over stillness-components. 52 | 53 | ```jsx 54 | 55 | 56 | 57 | ``` 58 | 59 | :::info Info 60 | 61 | Static components affect their children by default. There is no need to pass additional `type` values to the parent and child components when the `visible` on which they depend is the same. 62 | 63 | ::: -------------------------------------------------------------------------------- /website/docs/basic-concepts/max.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: max 3 | title: max 4 | --- 5 | 6 | > For special scenarios, sometimes you need to control the number of nodes in the cache, use `max` to achieve the effect of automatically refreshing the static components 7 | 8 | ## Example 9 | 10 | max' uses the [lru]() algorithm to automatically clear the oldest and least used nodes in the history when the static nodes reach the limit. thus improving performance 11 | 12 | Note that only the `` node at the first level of max is counted as a node, and all its children follow the parent node, so only the first level node in the system will participate in the `lru` memory 13 | 14 | ```jsx title="max" 15 | 16 | const App = () => { 17 | const [visible,setVisible] = useState(true); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | ``` -------------------------------------------------------------------------------- /website/docs/examples/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | title: Overview 4 | --- 5 | 6 | ## about 7 | 8 | The example here shows the actual application scenario of the component. -------------------------------------------------------------------------------- /website/docs/examples/nextjs/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":3 3 | } -------------------------------------------------------------------------------- /website/docs/examples/react-router/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":1 3 | } -------------------------------------------------------------------------------- /website/docs/examples/react-router/v6.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | title: v6 4 | --- 5 | 6 | In the **react-route v6** example, we use the [useOutlet](https://reactrouter.com/docs/en/v6/api#useoutlet) api to customize the processing return and force caching of all routes corresponding to the `outlet`, thus achieving the stillness effect 7 | 8 | -------------------------------------------------------------------------------- /website/docs/examples/simple/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":0 3 | } -------------------------------------------------------------------------------- /website/docs/examples/simple/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | title: simple 4 | --- 5 | 6 | The example on the home page is actually the most basic usage, of course, here to add a link to debug 7 | 8 | -------------------------------------------------------------------------------- /website/docs/examples/umi/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":2 3 | } -------------------------------------------------------------------------------- /website/docs/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: faq 3 | title: FAQ 4 | --- 5 | 6 | ## Question 7 | 8 | ### What is the problem with react-router when doing forward and backward routing? 9 | 10 | When the entire route node is cached, if a `history` operation such as `push` or `back` is performed, the original node is cached, so when the node is re-entered, clearing the cache before jumping will result in `componentDidMount`, and jumping before clearing the cache will result in `componentDidUpdate` 11 | 12 | ### Use react18 13 | 14 | The idea of `stillness-component` itself is to achieve local component caching without affecting the react rendering mechanism, so the component is not constrained by version, but because of the large number of hooks used internally, a minimum react version of 16.8 is required -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/1.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/10.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/11.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/12.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/13.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/2.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/3.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/4.png -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/5.gif -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/6.gif -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/7.gif -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/8.gif -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/i18n/zh-CN/docusaurus-plugin-content-blog/assets/9.gif -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/authors.yml: -------------------------------------------------------------------------------- 1 | leomYili: 2 | name: leomYili 3 | title: creator 4 | url: https://github.com/leomYili 5 | image_url: https://avatars.githubusercontent.com/u/17920032?v=4 -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-blog/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": { 3 | "message": "Blog", 4 | "description": "The title for the blog used in SEO" 5 | }, 6 | "description": { 7 | "message": "Blog", 8 | "description": "The description for the blog used in SEO" 9 | }, 10 | "sidebar.title": { 11 | "message": "文章列表", 12 | "description": "The label for the left sidebar" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current.json: -------------------------------------------------------------------------------- 1 | { 2 | "version.label": { 3 | "message": "Next", 4 | "description": "The label for version current" 5 | }, 6 | "sidebar.docs.category.Getting Started": { 7 | "message": "快速上手", 8 | "description": "The label for category Getting Started in sidebar docs" 9 | }, 10 | "sidebar.docs.category.Basic Concepts": { 11 | "message": "基础概念", 12 | "description": "The label for category Basic Concepts in sidebar docs" 13 | }, 14 | "sidebar.docs.category.API": { 15 | "message": "API", 16 | "description": "The label for category API in sidebar docs" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Components/Offscreen.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | id: offscreen 4 | title: Offscreen 5 | --- 6 | 7 | > 用``包裹需要静止的组件 8 | 9 | ## 使用 10 | 11 | ```jsx 12 | import { Offscreen } from 'react-stillness-component'; 13 | 14 | function App() { 15 | const [show, setShow] = useState(true); 16 | 17 | return ( 18 |
19 | 20 | // highlight-start 21 | 22 | 23 | 24 | // highlight-end 25 |
26 | ); 27 | } 28 | ``` 29 | 30 | ## Props 31 | 32 | - `visible`: 必选, **boolean** 类型,用于手动控制是否静止,只有为 `true` 时才会开始静止组件,初始化时如果为 `false` ,则自动开启**懒加载** 33 | - `type`: 可选, **string** 或者 **number** 类型,帮助你归类静止组件类型,便于管理与手动控制 34 | - `scrollReset`: 可选, **boolean** 类型,默认情况下,滚动位置将被自动保存,如果不需要这个功能,请设置为 `false`。 35 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Components/StillnessProvider.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | id: stillnessProvider 4 | title: StillnessProvider 5 | --- 6 | 7 | > `StillnessProvider` 是整个组件静止的核心,需要把它放置在所有 `Offscreen` 组件之外. 8 | 9 | ## 使用 10 | 11 | ```jsx 12 | import React from 'react'; 13 | import ReactDOM from 'react-dom'; 14 | import { StillnessProvider } from 'react-stillness-component'; 15 | import App from './App'; 16 | 17 | ReactDOM.render( 18 | 19 | 20 | , 21 | document.getElementById('root') 22 | ); 23 | ``` 24 | 25 | ## Props 26 | 27 | - `context`: 可选,用于配置当前静止上下文,默认为 `window` 28 | - `options`: 可选,全局配置参数,默认为 `{}` 29 | - `max`: 可选,静止状态的最大值,默认为 `-1` 30 | - `debugMode`: 可选,是否开启调试模式,默认为 `false` 31 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Components/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":0 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Decorators/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":1 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Decorators/connectStillness.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | id: connectStillness 4 | title: connectStillness 5 | --- 6 | 7 | > `connectStillness` 函数将 React 组件连接到静止组件控制中心,它向连接的组件提供从控制中心中获取的数据,以及它可以用来操作静止的各项功能,它不会修改传递给它的组件,相反,它返回一个新的连接组件. 8 | 9 | ## 使用 10 | 11 | ```jsx 12 | import { connectStillness } from 'react-stillness-component'; 13 | 14 | class Count extends Component { 15 | state = { 16 | count: 0, 17 | }; 18 | 19 | render() { 20 | const { isStillness, stillnessId, ...rest } = this.props; 21 | return
...
; 22 | } 23 | } 24 | 25 | const spec = { 26 | mounted: (props, contract) => { 27 | return 'mounted'; 28 | }, 29 | unmounted: (props, contract) => { 30 | return 'unmounted'; 31 | }, 32 | collect: (props, contract) => { 33 | return { 34 | isStillness: contract.isStillness(), 35 | stillnessId: contract.getStillnessId(), 36 | }; 37 | } 38 | }; 39 | 40 | export const WithCount = connectStillness(spec)(Count); 41 | ``` 42 | 43 | ## 参数 44 | 45 | - `spec`: 必选, **object** ,用于描述静止组件的行为,包括: 46 | - `mounted`: 必选, **function** 类型,用于描述静止组件的初始化行为,参数为: 47 | - `props`: 必选, **object** 类型,组件的 props 48 | - `contract`: 必选, **object** 类型,组件的 contract,更多信息请查看 [Contract State](api/contract-state.md) 49 | - `unmounted`: 可选, **function** 类型,用于描述静止组件的卸载行为,参数为: 50 | - `props`: 必选, **object** 类型,组件的 props 51 | - `contract`: 必选, **object** 类型,组件的 contract,更多信息请查看 [Contract State](api/contract-state.md) 52 | - `collect`: 必选, **function** 类型,用于描述静止组件的数据收集行为,参数为: 53 | - `props`: 必选, **object** 类型,组件的 props 54 | - `contract`: 必选, **object** 类型,组件的 contract,更多信息请查看 [Contract State](api/contract-state.md) 55 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Hooks/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":2 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Hooks/useStillness.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | id: useStillness 4 | title: useStillness 5 | --- 6 | 7 | > `useStillness` 将 React 组件连接到静止组件控制中心,它向连接的组件提供从控制中心中获取的数据,以及它可以用来操作静止的各项功能,你可以声明想要的属性与方法 8 | 9 | ## 使用 10 | 11 | ```jsx 12 | import { useStillness } from 'react-stillness-component'; 13 | 14 | function Count(props) { 15 | const [count, setCount] = useState(0); 16 | const collected = useStillness({ 17 | mounted: (contract) => { 18 | return 'mounted'; 19 | }, 20 | unmounted: (contract) => { 21 | return 'unmounted'; 22 | }, 23 | collect: (contract) => { 24 | return { 25 | isStillness: contract.isStillness(), 26 | stillnessId: contract.getStillnessId(), 27 | item: contract.getStillnessItem(), 28 | }; 29 | }, 30 | }); 31 | 32 | useEffect(() => { 33 | console.log(collected); 34 | }, [collected]); 35 | 36 | return
...
; 37 | } 38 | ``` 39 | 40 | ## 参数 41 | 42 | - `mounted`: 可选 **function** 类型,用于描述静止组件的初始化行为,参数为: 43 | - `props`: 必选, **object** 类型,组件的 props 44 | - `contract`: 必选, **object** 类型,组件的 contract,更多信息请查看 [Contract State](api/contract-state.md) 45 | - `unmounted`: 可选, **function** 类型,用于描述静止组件的卸载行为,参数为: 46 | - `props`: 必选, **object** 类型,组件的 props 47 | - `contract`: 必选, **object** 类型,组件的 contract,更多信息请查看 [Contract State](api/contract-state.md) 48 | - `collect`: 可选, **function** 类型,用于描述静止组件的数据收集行为,参数为: 49 | - `props`: 必选, **object** 类型,组件的 props 50 | - `contract`: 必选, **object** 类型,组件的 contract,更多信息请查看 [Contract State](api/contract-state.md) 51 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/Hooks/useStillnessManager.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | id: useStillnessManager 4 | title: useStillnessManager 5 | --- 6 | 7 | > `useStillnessManager` 提供了控制静止系统的权限,返回的实例对象中包含了对全部静止状态,contract等的访问. 8 | 9 | ## 使用 10 | 11 | ```jsx 12 | import { useStillnessManager } from 'react-stillness-component'; 13 | 14 | function Example() { 15 | const stillnessManager = useStillnessManager(); 16 | 17 | return
Example
18 | } 19 | ``` 20 | 21 | ## 实例(object) 22 | 23 | - `getStore`: 获取抽象的全局建模对象-store 24 | - `getMonitor`: 获取内部监听器方法 25 | - `getActions`: 获取内部全部行为 26 | - `dispatch`: 调用 `redux` 的 **dispatch** 方法 27 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/contract-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | id: contract-state 4 | title: Contract State 5 | --- 6 | 7 | > `Contract State` 是返回给 `collect`函数的参数,它将提供特定静止组件(一般是最近层级的``)的各种信息. 8 | 9 | ## 方法 10 | 11 | - `getStillnessId()`: 返回 **string** 类型,是当前静止组件的唯一标识 12 | - `getStillnessItem()`: 返回 **object** 类型,是当前静止组件的数据 13 | - `getStillnessType()`: 返回 **string** 类型,是当前静止组件的类型 14 | - `isStillness()`: 返回 **boolean**类型, 检查当前组件的静止状态 15 | - `unset(id?: string,type?: string)`: 不返回值,可以指定要卸载的静止组件的唯一标识和类型,也可以看做刷新组件 16 | - `clear()`: 不返回值,清除所有静止组件状态,也可以看做刷新组件 17 | - `resetMax(max: number)`: 重置全局静止组件最大值 18 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/api/global.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | id: global 4 | title: global 5 | --- 6 | 7 | 组件现在对外暴露了全局属性 - `INSTANCE_SYM` 该属性挂载在当前全局变量上(比如`window`) 8 | 9 | 也可以通过字符串 - `__REACT_STILLNESS_CONTEXT_INSTANCE__` 直接引用,目的是为了特殊场景下,不用通过hooks或者context来获取`manager`,从而支持了更强大的自定义能力 10 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/basic-concepts/contract.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: contract 3 | title: Contract 4 | --- 5 | 6 | > `contract` 意味着静止组件产生变化时的对外公开约定,它将提供特定静止组件的各种信息.它通过更新包装组件的 `props` 来响应静止状态更改. 7 | 8 | ## collect 9 | 10 | 对于每个需要监听静止状态的组件,可以自定义一个纯函数,并传递给 `connectStillness` 或 `useStillness`,静止组件将收集并在静止状态被修改时调用这个函数,并将返回值合并到 `props` 中. 11 | 12 | 示例如下: 13 | 14 | ```jsx title="Hoc" 15 | const spec = { 16 | // highlight-start 17 | collect: (props,contract) => ({ 18 | isStillness: contract.isStillness(), 19 | stillnessId: contract.getStillnessId(), 20 | }); 21 | // highlight-end 22 | }; 23 | 24 | connectStillness(spec); 25 | ``` 26 | 27 | 或者 28 | 29 | ```jsx title="Hooks" 30 | const spec = (props) => { 31 | return { 32 | // highlight-start 33 | collect: (contract) => ({ 34 | isStillness: contract.isStillness(), 35 | stillnessId: contract.getStillnessId(), 36 | }) 37 | // highlight-end 38 | } 39 | } 40 | 41 | function Count(props) { 42 | const collected = useStillness(spec(props)); 43 | ... 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/basic-concepts/items-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: items-types 3 | title: Items and Types 4 | --- 5 | 6 | > 当监听静止组件各种状态时,通常离不开对于 `Items` 与 `Types` 的使用. 7 | 8 | ## Items 9 | 10 | `Items` 是描述被静止内容的 **JavaScript 对象**.当开始静止组件时,你可以创建一个类似 `{type:"mounted",data:{...}}` 的对象.这个对象之后会传递给组件的 `props` 以便使用. 11 | 12 | ```jsx 13 | ... 14 | const spec = { 15 | mounted: (props, contract) => { 16 | return { 17 | type: 'mounted', 18 | data: { 19 | ... 20 | } 21 | }; 22 | }, 23 | unmounted: (props, contract) => { 24 | return { 25 | type: 'unmounted', 26 | data: { 27 | ... 28 | } 29 | }; 30 | }, 31 | collect: (props, contract) => { 32 | return { 33 | isStillness: contract.isStillness(), 34 | stillnessId: contract.getStillnessId(), 35 | }; 36 | }, 37 | }; 38 | 39 | connectStillness(spec) or useStillness(spec); 40 | ... 41 | ``` 42 | 43 | :::info 提示 44 | 45 | `Items` 并不是必须返回的,你可以根据自己的需求选择返回任何内容 46 | 47 | ::: 48 | 49 | ## Types 50 | 51 | `Type` 是一个 `string` 类型的值,用于标识应用中同一类别的静止组件.在应用中通常表现为 `type="card"` ,`Type` 不是必须的,当你有外部控制静止组件需求时,可以通过 `props` 传递 `type` 值. 52 | 53 | ```jsx 54 | 55 | 56 | 57 | ``` 58 | 59 | :::info 提示 60 | 61 | 静止组件默认父组件状态影响子组件,当父子组件所依赖的 `visible` 一致时,不需要额外给父子组件传递 `type` 值. 62 | 63 | ::: -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/basic-concepts/max.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: max 3 | title: max 4 | --- 5 | 6 | > 针对特殊场景,有时需要控制缓存的节点数量,使用 `max` 可以达到自动刷新静止组件的效果 7 | 8 | ## 示例 9 | 10 | `max`使用了 [lru]()算法, 当静止节点达到上限的时候,自动清除历史最老最少使用的节点,从而提高了性能 11 | 12 | 这里需要注意,max中只有第一层的``节点才会算作是一个节点,其子节点全部跟随父节点,因此,系统中只有第一层节点才会参与 `lru` 记忆 13 | 14 | ```jsx title="max" 15 | 16 | const App = () => { 17 | const [visible,setVisible] = useState(true); 18 | 19 | return ( 20 | 21 |
22 | 23 | 24 | 25 | ); 26 | } 27 | ``` 28 | 29 | When component C goes to rest, it automatically resets component A -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | title: 概述 4 | --- 5 | 6 | ## 关于 7 | 8 | 这里的例子展示了组件的实际应用场景. -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/nextjs/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":3 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/react-router/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":1 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/react-router/v5.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | title: v5 4 | --- 5 | 6 | ## React-router Route 静止路由 7 | 8 | 这个例子分别展示了 `state` 与 `dom` 的缓存 9 | 10 | 其中, `Home` 页签中的计数器不会因为路由变化而更新,因为它被静止了. 11 | 而 `list` 页签中的列表滚动之后会一直记忆上次的位置,因为它也被静止了. 12 | 13 | 当然,点击 **clear** 按钮之后,计数器和列表的缓存状态会被清空. 14 | 15 | 21 | 22 | ## React-router Switch 静止路由切换 23 | 24 | 这个例子中使用了 `Switch` 的相关特性 25 | 26 | :::info Title: StillnessSwitch 27 | 28 | 这里的 `StillnessSwitch` 是基于 `withRouter` 封装的组件,正如简述中所说, `react-stillness-component` 是基础库,可以在其上再次进行组合和封装 29 | 30 | 因为官方提供的 `switch` 会默认卸载掉其他路由,所以这里封装的组件在保留了 `switch` 的匹配功能之外,还针对路由做了缓存处理,这样每一个路由默认就是可以静止的,如果想自己控制,也可以通过各种原生能力来支持,比如:`props`,相比于原版的代码,仅仅只是不卸载其他路由而已 31 | 32 | 如果有更好的思路,欢迎讨论! 33 | 34 | ::: 35 | 36 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/react-router/v6.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | title: v6 4 | --- 5 | 6 | **react-route v6**的例子中, 我们使用了[useOutlet](https://reactrouter.com/docs/en/v6/api#useoutlet)api,自定义处理返回,强制缓存所有路由对应的`outlet`,从而达到静止效果 7 | 8 | 14 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/simple/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":0 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/simple/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | title: 简单实例 4 | --- 5 | 6 | 首页的例子实际上就是最基本的用法,当然,这里增加一个链接,以便调试 7 | 8 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/examples/umi/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position":2 3 | } -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-plugin-content-docs/current/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: faq 3 | title: 答疑 4 | --- 5 | 6 | ## 问题 7 | 8 | ### react-router 的路由做前进后退处理时的问题? 9 | 10 | 当缓存整个路由节点时,如果产生前进(`push`)或者后退(`back`)等`history`操作时,因为原来的节点已被缓存,所以再次进入该节点时,先清除缓存再跳转则会执行 `componentDidMount`,先跳转再清除缓存则会执行 `componentDidUpdate` 11 | 12 | ### 使用 react18 13 | 14 | `stillness-component` 本身的理念就是在不影响react渲染机制的前提下,实现局部组件缓存效果,因此,组件并不受版本制约,只是因为内部使用了大量的hooks写法,因此最低要求react版本为16.8 -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-theme-classic/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "link.title.Docs": { 3 | "message": "文档", 4 | "description": "The title of the footer links column with title=Docs in the footer" 5 | }, 6 | "link.title.Community": { 7 | "message": "社区", 8 | "description": "The title of the footer links column with title=Community in the footer" 9 | }, 10 | "link.title.More": { 11 | "message": "More", 12 | "description": "The title of the footer links column with title=More in the footer" 13 | }, 14 | "link.item.label.Stack Overflow": { 15 | "message": "Stack Overflow", 16 | "description": "The label of footer link with label=Stack Overflow linking to https://stackoverflow.com/questions/tagged/react-stillness-component" 17 | }, 18 | "link.item.label.GitHub": { 19 | "message": "GitHub", 20 | "description": "The label of footer link with label=GitHub linking to https://github.com/leomYili/react-stillness-component" 21 | }, 22 | "copyright": { 23 | "message": "Copyright © 2022 My Project, Inc. Built with Docusaurus.", 24 | "description": "The footer copyright" 25 | }, 26 | "link.item.label.Get Started": { 27 | "message": "快速上手", 28 | "description": "The label of footer link with label=Get Started linking to /docs/category/getting-started" 29 | }, 30 | "link.item.label.API": { 31 | "message": "API", 32 | "description": "The label of footer link with label=API linking to /docs/category/api" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /website/i18n/zh-CN/docusaurus-theme-classic/navbar.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": { 3 | "message": "React-stillness-component", 4 | "description": "The title in the navbar" 5 | }, 6 | "item.label.GitHub": { 7 | "message": "GitHub", 8 | "description": "Navbar item with label GitHub" 9 | }, 10 | "item.label.Docs": { 11 | "message": "文档", 12 | "description": "Navbar item with label Docs" 13 | }, 14 | "item.label.Example": { 15 | "message": "示例", 16 | "description": "Navbar item with label Example" 17 | }, 18 | "item.label.API": { 19 | "message": "API", 20 | "description": "Navbar item with label API" 21 | }, 22 | "item.label.Examples": { 23 | "message": "示例", 24 | "description": "Navbar item with label Examples" 25 | }, 26 | "item.label.releases": { 27 | "message": "releases", 28 | "description": "Navbar item with label releases" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "start:zh": "yarn run clean && yarn run start --locale zh-CN", 9 | "build": "docusaurus build", 10 | "build:gh": "cross-env BASE_URL='/react-stillness-component/' docusaurus build", 11 | "build:vercel": "cross-env BASE_URL='/' docusaurus build", 12 | "swizzle": "docusaurus swizzle", 13 | "deploy": "docusaurus deploy", 14 | "clean": "docusaurus clear", 15 | "serve": "docusaurus serve", 16 | "write-translations": "docusaurus write-translations", 17 | "wtz": "docusaurus write-translations --locale zh-CN", 18 | "write-heading-ids": "docusaurus write-heading-ids", 19 | "typecheck": "tsc" 20 | }, 21 | "dependencies": { 22 | "@docusaurus/core": "2.0.0-beta.18", 23 | "@docusaurus/preset-classic": "2.0.0-beta.18", 24 | "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.18", 25 | "@mdx-js/react": "^1.6.22", 26 | "clsx": "^1.1.1", 27 | "prism-react-renderer": "^1.3.1", 28 | "react": "^17.0.2", 29 | "react-dom": "^17.0.2", 30 | "react-live": "^2.4.1", 31 | "react-stillness-component": "^0.8.1" 32 | }, 33 | "devDependencies": { 34 | "@docusaurus/module-type-aliases": "2.0.0-beta.18", 35 | "@tsconfig/docusaurus": "^1.0.5", 36 | "cross-env": "^7.0.3", 37 | "typescript": "^4.6.3" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.5%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | docs: [ 17 | 'intro', 18 | 'get-started', 19 | { 20 | type: 'category', 21 | label: 'Basic Concepts', 22 | link: { 23 | type: 'generated-index', 24 | }, 25 | collapsed: true, 26 | items: ['basic-concepts/items-types', 'basic-concepts/contract','basic-concepts/max'], 27 | }, 28 | { 29 | type: 'category', 30 | label: 'API', 31 | link: { 32 | type: 'generated-index', 33 | }, 34 | collapsed: true, 35 | items: [ 36 | { 37 | type: 'autogenerated', 38 | dirName: 'api', 39 | }, 40 | ], 41 | }, 42 | 'faq', 43 | ], 44 | examples: [ 45 | { 46 | type: 'autogenerated', 47 | dirName: 'examples', 48 | }, 49 | ], 50 | }; 51 | 52 | module.exports = sidebars; 53 | -------------------------------------------------------------------------------- /website/src/components/HomepageEditor/styles.module.css: -------------------------------------------------------------------------------- 1 | .second { 2 | width: 100%; 3 | } 4 | 5 | .title { 6 | font-size: 28px; 7 | margin-bottom: 10px; 8 | margin-top: 20px; 9 | text-align: center; 10 | } 11 | 12 | .subTitle { 13 | font-size: 14px; 14 | white-space: pre-line; 15 | font-weight: 300; 16 | margin: 5px auto; 17 | text-align: center; 18 | } 19 | 20 | .liveContainer { 21 | margin: 0 auto; 22 | max-width: 80vw; 23 | margin-bottom: 80px; 24 | border-radius: 8px; 25 | box-shadow: 0 5px 20px rgb(185 185 185 / 50%); 26 | } 27 | 28 | .liveWrapper { 29 | display: flex; 30 | flex-direction: row; 31 | justify-content: stretch; 32 | align-items: stretch; 33 | flex-wrap: wrap; 34 | padding: 10px; 35 | } 36 | 37 | .info { 38 | padding: 10px; 39 | } 40 | 41 | .column { 42 | flex-basis: 50%; 43 | width: 50%; 44 | max-width: 50%; 45 | } 46 | 47 | .editor { 48 | background-color: #3e3e3e; 49 | height: 40rem; 50 | overflow: auto; 51 | } 52 | 53 | .preview { 54 | background-color: #f6f8fa; 55 | padding: 10px; 56 | border-left: 1px solid #efefef; 57 | } 58 | 59 | .codeTop { 60 | width: 100%; 61 | height: 30px; 62 | background: #fff; 63 | border-bottom: 1px solid #efefef; 64 | line-height: 34px; 65 | } 66 | 67 | .codeTop i { 68 | display: inline-block; 69 | width: 12px; 70 | height: 12px; 71 | border-radius: 50%; 72 | border-width: 1px; 73 | margin-left: 10px; 74 | } 75 | 76 | .dot1 { 77 | background: #f25f58; 78 | border-color: #d94940; 79 | } 80 | 81 | .dot2 { 82 | background: #fbbe3c; 83 | border-color: #d69e23; 84 | } 85 | 86 | .dot3 { 87 | background: #58cb42; 88 | border-color: #1dad2c; 89 | } 90 | 91 | @media (max-width: 600px) { 92 | .liveContainer { 93 | flex-direction: column; 94 | } 95 | 96 | .column { 97 | flex-basis: auto; 98 | width: 100%; 99 | max-width: 100%; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /website/src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './styles.module.css'; 4 | 5 | type FeatureItem = { 6 | title: string; 7 | Svg: React.ComponentType>; 8 | description: JSX.Element; 9 | }; 10 | 11 | const FeatureList: FeatureItem[] = [ 12 | { 13 | title: 'Easy to Use', 14 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, 15 | description: ( 16 | <> 17 | Docusaurus was designed from the ground up to be easily installed and 18 | used to get your website up and running quickly. 19 | 20 | ), 21 | }, 22 | { 23 | title: 'Focus on What Matters', 24 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, 25 | description: ( 26 | <> 27 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 28 | ahead and move your docs into the docs directory. 29 | 30 | ), 31 | }, 32 | { 33 | title: 'Powered by React', 34 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, 35 | description: ( 36 | <> 37 | Extend or customize your website layout by reusing React. Docusaurus can 38 | be extended while reusing the same header and footer. 39 | 40 | ), 41 | }, 42 | ]; 43 | 44 | function Feature({title, Svg, description}: FeatureItem) { 45 | return ( 46 |
47 |
48 | 49 |
50 |
51 |

{title}

52 |

{description}

53 |
54 |
55 | ); 56 | } 57 | 58 | export default function HomepageFeatures(): JSX.Element { 59 | return ( 60 |
61 |
62 |
63 | {FeatureList.map((props, idx) => ( 64 | 65 | ))} 66 |
67 |
68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /website/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #158fcc; 10 | --ifm-color-primary-dark: #1381b8; 11 | --ifm-color-primary-darker: #127aad; 12 | --ifm-color-primary-darkest: #0f648f; 13 | --ifm-color-primary-light: #179de0; 14 | --ifm-color-primary-lighter: #1ba3e8; 15 | --ifm-color-primary-lightest: #3ab0eb; 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 20 | [data-theme='dark'] { 21 | --ifm-color-primary: #bbeafe; 22 | --ifm-color-primary-dark: #90ddfd; 23 | --ifm-color-primary-darker: #7ad6fd; 24 | --ifm-color-primary-darkest: #39c2fc; 25 | --ifm-color-primary-light: #e6f7ff; 26 | --ifm-color-primary-lighter: #fcfeff; 27 | --ifm-color-primary-lightest: #ffffff; 28 | } 29 | 30 | .docusaurus-highlight-code-line { 31 | background-color: rgb(255, 251, 221); 32 | display: block; 33 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 34 | padding: 0 var(--ifm-pre-padding); 35 | } 36 | 37 | [data-theme='dark'] .docusaurus-highlight-code-line { 38 | background-color: rgba(0, 0, 0, 0.3); 39 | } 40 | -------------------------------------------------------------------------------- /website/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /website/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Head from '@docusaurus/Head'; 4 | import Layout from '@theme/Layout'; 5 | import Link from '@docusaurus/Link'; 6 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 7 | import styles from './index.module.css'; 8 | import { HomepageEditor } from '../components/HomepageEditor'; 9 | import Translate, { translate } from '@docusaurus/Translate'; 10 | 11 | function HomepageHeader() { 12 | const { siteConfig } = useDocusaurusContext(); 13 | return ( 14 |
15 |
16 |

{siteConfig.title}

17 |

18 | 22 | {'{text}'} 23 | 24 |

25 |
26 | 30 | Get Started 31 | 32 |
33 |
34 |
35 | ); 36 | } 37 | 38 | export default function Home(): JSX.Element { 39 | const { siteConfig } = useDocusaurusContext(); 40 | return ( 41 | 45 | 46 |
47 | 48 |
49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /website/src/theme/CodeBlock/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | .codeBlockContainer { 9 | margin-bottom: var(--ifm-leading); 10 | box-shadow: 0 5px 20px rgb(185 185 185 / 50%); 11 | } 12 | 13 | .codeBlockContent { 14 | position: relative; 15 | /* rtl:ignore */ 16 | direction: ltr; 17 | border-radius: var(--ifm-global-radius); 18 | } 19 | 20 | .codeBlockTitle { 21 | border-bottom: 1px solid var(--ifm-color-emphasis-300); 22 | font-size: var(--ifm-code-font-size); 23 | font-weight: 500; 24 | padding: 0.75rem var(--ifm-pre-padding); 25 | border-top-left-radius: var(--ifm-global-radius); 26 | border-top-right-radius: var(--ifm-global-radius); 27 | } 28 | 29 | .codeBlock { 30 | margin: 0; 31 | padding: 0; 32 | background-color: inherit; 33 | } 34 | 35 | .codeBlockTitle + .codeBlockContent .codeBlock { 36 | border-top-left-radius: 0; 37 | border-top-right-radius: 0; 38 | } 39 | 40 | .codeBlockStandalone { 41 | padding: 0; 42 | } 43 | 44 | .codeBlockLines { 45 | font: inherit; 46 | /* rtl:ignore */ 47 | float: left; 48 | min-width: 100%; 49 | padding: var(--ifm-pre-padding); 50 | } 51 | 52 | @media print { 53 | .codeBlockLines { 54 | white-space: pre-wrap; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /website/src/theme/DocPage/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | :root { 9 | --doc-sidebar-width: 300px; 10 | --doc-sidebar-hidden-width: 30px; 11 | } 12 | 13 | .docPage, 14 | .docMainContainer { 15 | width: 100%; 16 | } 17 | 18 | .docsWrapper, 19 | .docPage, 20 | .docMainContainer { 21 | display: flex; 22 | } 23 | 24 | .docSidebarContainer { 25 | display: none; 26 | } 27 | 28 | @media (min-width: 997px) { 29 | .docMainContainer { 30 | flex-grow: 1; 31 | max-width: calc(100% - var(--doc-sidebar-width)); 32 | } 33 | 34 | .docMainContainerEnhanced { 35 | max-width: calc(100% - var(--doc-sidebar-hidden-width)); 36 | } 37 | 38 | .docSidebarContainer { 39 | display: block; 40 | width: var(--doc-sidebar-width); 41 | margin-top: calc(-1 * var(--ifm-navbar-height)); 42 | border-right: 1px solid var(--ifm-toc-border-color); 43 | will-change: width; 44 | transition: width var(--ifm-transition-fast) ease; 45 | clip-path: inset(0); 46 | } 47 | 48 | .docSidebarContainerHidden { 49 | width: var(--doc-sidebar-hidden-width); 50 | cursor: pointer; 51 | } 52 | 53 | .collapsedDocSidebar { 54 | position: sticky; 55 | top: 0; 56 | height: 100%; 57 | max-height: 100vh; 58 | display: flex; 59 | align-items: center; 60 | justify-content: center; 61 | transition: background-color var(--ifm-transition-fast) ease; 62 | } 63 | 64 | .collapsedDocSidebar:hover, 65 | .collapsedDocSidebar:focus { 66 | background-color: var(--ifm-color-emphasis-200); 67 | } 68 | 69 | .expandSidebarButtonIcon { 70 | transform: rotate(0); 71 | } 72 | 73 | [dir='rtl'] .expandSidebarButtonIcon { 74 | transform: rotate(180deg); 75 | } 76 | 77 | [data-theme='dark'] .collapsedDocSidebar:hover, 78 | [data-theme='dark'] .collapsedDocSidebar:focus { 79 | background-color: var(--collapse-button-bg-color-dark); 80 | } 81 | 82 | .docItemWrapperEnhanced { 83 | max-width: calc( 84 | var(--ifm-container-width) + var(--doc-sidebar-width) 85 | ) !important; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/.nojekyll -------------------------------------------------------------------------------- /website/static/baidu_verify_code-7U9vS3Ps0V.html: -------------------------------------------------------------------------------- 1 | 1ef3e69d04277a91fbc00f2277354698 -------------------------------------------------------------------------------- /website/static/img/code1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/code1.png -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/favicon.ico -------------------------------------------------------------------------------- /website/static/img/intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/intro.gif -------------------------------------------------------------------------------- /website/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/logo.png -------------------------------------------------------------------------------- /website/static/img/real-dom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/real-dom.png -------------------------------------------------------------------------------- /website/static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /website/static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | --------------------------------------------------------------------------------