├── .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 | [](https://www.npmjs.com/package/react-stillness-component) [](https://bundlephobia.com/result?p=react-stillness-component@latest) [](https://github.com/leomYili/react-stillness-component/blob/main/LICENSE)[](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 | You need to enable JavaScript to run this app.
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 |
{
38 | setCount((count) => count + 1);
39 | }}
40 | >
41 | Add
42 |
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 | {
22 | setVisible(!visible);
23 | setCount(count + 1);
24 | }}
25 | >
26 | 切换显隐状态
27 |
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 |
51 | +
52 |
53 |
54 | -
55 |
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 |
{
19 | this.setState({ count: this.state.count + 1 });
20 | }}
21 | >
22 | Add
23 |
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 |
{
37 | setCount((count) => count + 1);
38 | }}
39 | >
40 | Add
41 |
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 |
{
19 | console.log('点击??????');
20 | setCount((count) => count + 1);
21 | }}
22 | >
23 | Add
24 |
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 | You need to enable JavaScript to run this app.
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 | setShow((show) => !show)}>Toggle
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 |
{
46 | setCount((count) => count + 1);
47 | }}
48 | >
49 | Add
50 |
51 |
52 | );
53 | }
54 |
55 | function App() {
56 | return (
57 |
58 |
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 | {
13 | setCount(count + 1);
14 | }}
15 | >
16 | add
17 |
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 | {
11 | setCount(count + 1);
12 | }}
13 | >
14 | add
15 |
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 |
{
20 | this.setState({ count: this.state.count + 1 });
21 | }}
22 | >
23 | Add
24 |
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 |
{
30 | setCount((count) => count + 1);
31 | }}
32 | >
33 | Add
34 |
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 | {
14 | manager.getActions().triggerUnset({ type: '/home' });
15 | }}
16 | >
17 | 清除home child data
18 |
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 |
18 | Hello Vite + React!
19 |
20 |
21 | setCount((count) => count + 1)}
24 | >
25 | count is: {count}
26 |
27 |
28 |
29 |
30 | Edit App.tsx
and save to test HMR updates.
31 |
32 | {
34 | setVisible(!visible);
35 | }}
36 | >
37 | switch
38 |
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 | setVisible(!visible)}/>
43 |
44 | );
45 | };
46 |
47 | const { queryByText,getByTestId,debug } = render( );
48 |
49 | const user = userEvent.setup();
50 |
51 | await user.click(getByTestId('toggle'));
52 | await user.click(getByTestId('toggle'));
53 |
54 | expect(queryByText('unmounted')).toBeInTheDocument();
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/decorators/__tests__/withNodeBridge.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import { jest } from '@jest/globals';
4 |
5 | import { withNodeBridge } from '../withNodeBridge';
6 | import { OffscreenInnerProps } from '../../components';
7 |
8 | describe('withNodeBridge', () => {
9 | it('can apply to a react class component', () => {
10 | class TestClass extends React.Component<
11 | React.PropsWithChildren
12 | > {}
13 |
14 | const DecoratedClass = withNodeBridge(TestClass);
15 |
16 | expect(DecoratedClass).toBeDefined();
17 | });
18 |
19 | it('can apply to a function component', () => {
20 | const Component: React.FC<
21 | React.PropsWithChildren
22 | > = () => null;
23 |
24 | const DecoratedClass = withNodeBridge(Component);
25 |
26 | expect(DecoratedClass).toBeDefined();
27 | });
28 |
29 | it('throw an error if rendered', () => {
30 | console.error = jest.fn();
31 |
32 | class TestClass extends React.Component<
33 | React.PropsWithChildren
34 | > {}
35 |
36 | const DecoratedClass = withNodeBridge(TestClass);
37 |
38 | expect(() => {
39 | render( );
40 | }).toThrow(/Expected stillness component context/);
41 | });
42 |
43 | it('throw an error if rendered', () => {
44 | console.error = jest.fn();
45 |
46 | class TestClass extends React.Component<
47 | React.PropsWithChildren
48 | > {}
49 |
50 | const DecoratedClass = withNodeBridge(TestClass);
51 |
52 | expect(() => {
53 | render( );
54 | }).toThrow(/Expected stillness component context/);
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/decorators/connectStillness.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentType as RComponentType, PropsWithChildren } from 'react';
2 |
3 | import invariant from 'invariant';
4 | import { decorateHandler } from './decorateHandler';
5 | import { StillnessContractImpl, StillnessHandleImpl } from '../internals';
6 | import { isUndefined } from '../utils';
7 | import {
8 | UniqueId,
9 | StillnessSpec,
10 | StillnessCollector,
11 | StillnessManager,
12 | FactoryOrInstance,
13 | StillnessComponentEnhancer,
14 | } from '../types';
15 |
16 | export function connectStillness<
17 | RequiredProps,
18 | CollectedProps = any,
19 | ResObject = any
20 | >(
21 | specArg: FactoryOrInstance>
22 | ): StillnessComponentEnhancer> {
23 | invariant(!isUndefined(specArg), 'spec is required');
24 |
25 | const _spec =
26 | typeof specArg === 'function'
27 | ? (specArg as () => StillnessSpec)()
28 | : (specArg as StillnessSpec);
29 |
30 | return function decorateStillness<
31 | ComponentType extends RComponentType
32 | >(DecoratedComponent: ComponentType) {
33 | return decorateHandler({
34 | DecoratedComponent,
35 | containerDisplayName: 'Stillness',
36 | createHandle: (manager, contract) =>
37 | new StillnessHandleImpl(_spec || {}, manager, contract),
38 | createContract: (manager: StillnessManager) =>
39 | new StillnessContractImpl(manager),
40 | collect: _spec.collect,
41 | });
42 | } as any as StillnessComponentEnhancer>;
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/decorators/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './connectStillness';
2 | export * from './withNodeBridge';
3 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/HandleImpl.ts:
--------------------------------------------------------------------------------
1 | import { RefObject } from 'react';
2 | import {
3 | StillnessHookSpec,
4 | StillnessManager,
5 | StillnessContract,
6 | Handle,
7 | } from '../types';
8 | import { isFunction } from '../utils';
9 |
10 | export class HandleImpl implements Handle {
11 | private spec: StillnessHookSpec;
12 | private manager: StillnessManager;
13 | private contract: StillnessContract;
14 |
15 | constructor(
16 | spec: StillnessHookSpec,
17 | manager: StillnessManager,
18 | contract: StillnessContract
19 | ) {
20 | this.spec = spec;
21 | this.manager = manager;
22 | this.contract = contract;
23 | }
24 |
25 | public mount = () => {
26 | let item;
27 |
28 | if(this.spec?.mounted && isFunction(this.spec?.mounted)){
29 | item = this.spec?.mounted(this.contract);
30 | }
31 |
32 | return item;
33 | };
34 |
35 | public unmount = () => {
36 | let item;
37 |
38 | if (this.spec?.unmounted && isFunction(this.spec?.unmounted)) {
39 | item = this.spec?.unmounted(this.contract);
40 | }
41 |
42 | return item;
43 | };
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/__tests__/HandleImpl.spec.ts:
--------------------------------------------------------------------------------
1 | import { HandleImpl } from '../HandleImpl';
2 | import { createStillnessManager } from '../../core/createStillnessManager';
3 | import { StillnessContractImpl } from '../../internals/StillnessContractImpl';
4 |
5 | describe('HandleImpl', () => {
6 | const spec = {
7 | mounted: (contract) => `mounted`,
8 | unmounted: (contract) =>
9 | `unmounted`,
10 | collect: (contract) => {
11 | return {
12 | item: contract.getItem(),
13 | };
14 | },
15 | };
16 | it('can be constructed', () => {
17 | const manager = createStillnessManager();
18 | const contract = new StillnessContractImpl(manager);
19 |
20 | const handle = new HandleImpl(spec, manager, contract);
21 |
22 | expect(handle).toBeDefined();
23 | });
24 |
25 | it('When the parameters passed in do not meet the requirements, nothing is returned',() => {
26 | let mockManager: any = jest.fn();
27 | let mockContract: any = jest.fn().mockReturnValue({
28 | getItem: () => {
29 | return 'test2';
30 | },
31 | });
32 | const handle = new HandleImpl({},mockManager,mockContract)
33 | expect(handle.mount()).toBeUndefined();
34 | expect(handle.unmount()).toBeUndefined();
35 | })
36 |
37 | it('Automatically update item when changing props', () => {
38 | let mockManager: any = jest.fn();
39 | let mockContract: any = jest.fn().mockReturnValue({
40 | getItem: () => {
41 | return 'test2';
42 | },
43 | });
44 | const handle = new HandleImpl(spec, mockManager, mockContract);
45 | const result1 = handle.mount();
46 |
47 | expect(result1).toEqual('mounted');
48 |
49 | const result2 = handle.unmount();
50 |
51 | expect(result2).toEqual('unmounted');
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/__tests__/useOptionalFactory.spec.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useOptionalFactory } from '../useOptionalFactory';
3 | import { act, renderHook } from '@testing-library/react-hooks';
4 |
5 | const useTest = () => {
6 | const [count, setCount] = React.useState(0);
7 |
8 | const addCount = () => {
9 | setCount(count + 1);
10 | };
11 |
12 | const optionFactoryFn = useOptionalFactory(
13 | () => ({
14 | collect: () => {
15 | return {};
16 | },
17 | }),
18 | [count]
19 | );
20 |
21 | return { addCount, optionFactoryFn };
22 | };
23 |
24 | describe('useOptionalFactory', () => {
25 | let hook;
26 | it('Depending on the variation of the dependency value, different results are generated', () => {
27 | act(() => {
28 | hook = renderHook(() => useTest());
29 | });
30 |
31 | let memoValue = hook.result.current.optionFactoryFn;
32 |
33 | act(() => {
34 | hook.result.current.addCount();
35 | });
36 |
37 | expect(memoValue).not.toStrictEqual(hook.result.current.optionFactoryFn);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/__tests__/useStillnessManager.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import { jest } from '@jest/globals';
4 |
5 | import { useStillnessManager } from '../useStillnessManager';
6 |
7 | describe('useStillnessManager', () => {
8 | it('Error is reported when there is no context', () => {
9 | console.error = jest.fn();
10 |
11 | const Func = () => {
12 | useStillnessManager();
13 | return
;
14 | };
15 |
16 | expect(() => {
17 | render( );
18 | }).toThrow(/Expected stillness component context/i);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './useStillnessManager';
2 | export * from './useIsomorphicLayoutEffect';
3 | export * from './useStillness';
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useCollectedProps.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useContext } from 'react';
2 |
3 | import { useCollector } from './useCollector';
4 | import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
5 | import { StillnessNodeContext } from '../core';
6 | import {
7 | StillnessContract,
8 | Handle,
9 | StillnessMonitor,
10 | UniqueId,
11 | HandlerContract,
12 | } from '../types';
13 | import { isFunction } from '../utils';
14 |
15 | export function useCollectedProps(
16 | collector: ((contract: StillnessContract) => Collected) | undefined,
17 | monitor: StillnessMonitor,
18 | contract: StillnessContract & HandlerContract,
19 | handle: Handle
20 | ): Collected {
21 | const [collected, updateCollected] = useCollector(
22 | contract,
23 | collector || (() => ({} as Collected))
24 | );
25 | const { stillnessParentId } = useContext(StillnessNodeContext);
26 | const unsubscribe: any = useRef(null);
27 |
28 | useIsomorphicLayoutEffect(
29 | function subscribeToMonitorStateChange() {
30 | if (isFunction(unsubscribe.current)) {
31 | unsubscribe?.current();
32 | }
33 |
34 | unsubscribe.current = monitor.subscribeToStateChange(
35 | () => {
36 | const parentIsStillness = monitor.isStillness(stillnessParentId);
37 | if (!parentIsStillness) {
38 | contract.receiveItem(handle.unmount());
39 | } else {
40 | contract.receiveItem(handle.mount());
41 | }
42 |
43 | updateCollected();
44 | },
45 | {
46 | parentId: stillnessParentId,
47 | }
48 | );
49 |
50 | return () => {
51 | if (isFunction(unsubscribe.current)) {
52 | unsubscribe.current();
53 | }
54 | };
55 | },
56 | [monitor, updateCollected, stillnessParentId, handle]
57 | );
58 |
59 | return collected;
60 | }
61 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useCollector.ts:
--------------------------------------------------------------------------------
1 | import equal from 'fast-deep-equal';
2 | import { useState, useCallback, useRef } from 'react';
3 | import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
4 |
5 | export function useCollector(
6 | contract: T,
7 | collect: (contract: T) => S
8 | ): [S, () => void] {
9 | const [collected, setCollected] = useState(() => collect(contract));
10 |
11 | const updateCollected = useCallback(() => {
12 | const nextValue = collect(contract);
13 |
14 | if (!equal(collected, nextValue)) {
15 | setCollected((prevState) => {
16 | return { ...prevState, ...nextValue };
17 | });
18 | }
19 | }, [collected, contract]);
20 |
21 | useIsomorphicLayoutEffect(updateCollected);
22 |
23 | return [collected, updateCollected];
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useIsomorphicLayoutEffect.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useLayoutEffect } from 'react'
2 |
3 | // React currently throws a warning when using useLayoutEffect on the server.
4 | // To get around it, we can conditionally useEffect on the server (no-op) and
5 | // useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
6 | // subscription callback always has the selector from the latest render commit
7 | // available, otherwise a store update may happen between render and the effect,
8 | // which may cause missed updates; we also must ensure the store subscription
9 | // is created synchronously, otherwise a store update may occur before the
10 | // subscription is created and an inconsistent state may be observed
11 |
12 | // Matches logic in React's `shared/ExecutionEnvironment` file
13 | export const canUseDOM = !!(
14 | typeof window !== 'undefined' &&
15 | typeof window.document !== 'undefined' &&
16 | typeof window.document.createElement !== 'undefined'
17 | )
18 |
19 | export const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useOptionalFactory.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { FactoryOrInstance } from '../types';
3 |
4 | export function useOptionalFactory(
5 | arg: FactoryOrInstance,
6 | deps?: unknown[]
7 | ): T {
8 | const memoDeps = [...(deps || [])];
9 | if (deps == null && typeof arg !== 'function') {
10 | memoDeps.push(arg);
11 | }
12 | return useMemo(() => {
13 | return typeof arg === 'function' ? (arg as () => T)() : (arg as T);
14 | }, memoDeps);
15 | }
16 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useReceive.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useContext } from 'react';
2 |
3 | import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
4 | import { StillnessNodeContext } from '../core';
5 | import {
6 | StillnessContract,
7 | StillnessHookSpec,
8 | HandlerContract,
9 | } from '../types';
10 |
11 | export function useReceive(
12 | spec: StillnessHookSpec,
13 | contract: StillnessContract & HandlerContract
14 | ) {
15 | const { stillnessParentId } = useContext(StillnessNodeContext);
16 |
17 | const updateReceived = useCallback(() => {
18 | if (contract.getStillnessId() === stillnessParentId) {
19 | return;
20 | }
21 |
22 | contract.receiveId(stillnessParentId);
23 | }, [spec, contract, stillnessParentId]);
24 |
25 | useIsomorphicLayoutEffect(updateReceived);
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useStillness.ts:
--------------------------------------------------------------------------------
1 | import invariant from 'invariant';
2 | import { useCollectedProps } from './useCollectedProps';
3 | import { StillnessHookSpec, FactoryOrInstance } from '../types';
4 | import { useOptionalFactory } from './useOptionalFactory';
5 | import { useStillnessContract } from './useStillnessContract';
6 | import { useStillnessMonitor } from './useStillnessMonitor';
7 | import { useStillnessManager } from './useStillnessManager';
8 | import { useStillnessHandle } from './useStillnessHandle';
9 | import { isUndefined } from '../utils';
10 |
11 | export function useStillness(
12 | specArg: FactoryOrInstance>,
13 | deps?: unknown[]
14 | ): CollectedProps {
15 | invariant(!isUndefined(specArg), 'spec is required');
16 |
17 | const stillnessManager = useStillnessManager();
18 | const spec = useOptionalFactory(specArg, deps);
19 | const contract = useStillnessContract(spec, stillnessManager);
20 | const monitor = useStillnessMonitor(stillnessManager);
21 | const handle = useStillnessHandle(spec, contract);
22 |
23 | return useCollectedProps(spec.collect, monitor, contract, handle);
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useStillnessContract.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 |
3 | import { useReceive } from './useReceive';
4 | import { StillnessContractImpl } from '../internals';
5 | import {
6 | StillnessContract,
7 | HandlerContract,
8 | StillnessManager,
9 | StillnessHookSpec,
10 | } from '../types';
11 |
12 | export function useStillnessContract(
13 | spec: StillnessHookSpec,
14 | stillnessManager: StillnessManager
15 | ): StillnessContract & HandlerContract {
16 | const contract = useMemo(() => {
17 | return new StillnessContractImpl(stillnessManager);
18 | }, [stillnessManager]);
19 |
20 | useReceive(spec, contract);
21 |
22 | return contract;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useStillnessHandle.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 |
3 | import { HandleImpl } from './HandleImpl';
4 | import { Handle, StillnessHookSpec, StillnessContract } from '../types';
5 | import { useStillnessManager } from './useStillnessManager';
6 |
7 | export function useStillnessHandle(
8 | spec: StillnessHookSpec,
9 | contract: StillnessContract
10 | ): Handle {
11 | const stillnessManager = useStillnessManager();
12 | return useMemo(() => {
13 | return new HandleImpl(spec, stillnessManager, contract);
14 | }, [spec, stillnessManager, contract]);
15 | }
16 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useStillnessManager.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 | import invariant from 'invariant';
3 | import { StillnessManager } from '../types';
4 | import { StillnessContext } from '../core';
5 |
6 | export function useStillnessManager(): StillnessManager {
7 | const { stillnessManager } = useContext(StillnessContext);
8 | invariant(stillnessManager != null, 'Expected stillness component context');
9 | return stillnessManager;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/hooks/useStillnessMonitor.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 |
3 | import { StillnessMonitor, StillnessManager } from '../types';
4 |
5 | export function useStillnessMonitor(
6 | stillnessManager: StillnessManager
7 | ): StillnessMonitor {
8 | return useMemo(() => stillnessManager.getMonitor(), [stillnessManager]);
9 | }
10 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './decorators';
2 | export * from './hooks';
3 | export * from './types';
4 | export * from './core';
5 | export * from './components';
--------------------------------------------------------------------------------
/packages/react-stillness/src/internals/StillnessContractImpl.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Identifier,
3 | StillnessMonitor,
4 | StillnessActions,
5 | StillnessContract,
6 | StillnessManager,
7 | UniqueId,
8 | UnsetParams,
9 | } from '../types';
10 | import { isUndefined } from '../utils';
11 |
12 | export class StillnessContractImpl implements StillnessContract {
13 | private internalMonitor: StillnessMonitor;
14 | private internalActions: StillnessActions;
15 | private id: UniqueId | undefined;
16 | private item: any;
17 |
18 | public constructor(manager: StillnessManager) {
19 | this.internalMonitor = manager.getMonitor();
20 | this.internalActions = manager.getActions();
21 | }
22 |
23 | public receiveId(id: UniqueId | undefined): void {
24 | this.id = id;
25 | }
26 |
27 | public receiveItem(item: any): void {
28 | this.item = item;
29 | }
30 |
31 | public isStillness = (): boolean => {
32 | return this.internalMonitor.isStillness(this.id);
33 | };
34 | public getStillnessId = (): UniqueId | undefined => {
35 | return this.id;
36 | };
37 | public getStillnessType = (): Identifier | undefined => {
38 | return this.internalMonitor.getStillnessType(this.id);
39 | };
40 | public getStillnessItem = () => {
41 | return this.item;
42 | };
43 | public unset = (params: UnsetParams): void => {
44 | this.internalActions.triggerUnset(params);
45 | };
46 | public clear = (): void => {
47 | this.internalActions.triggerClear();
48 | };
49 | public resetMax = (max: number): void => {
50 | this.internalActions.resetMax({ max });
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/internals/StillnessHandleImpl.ts:
--------------------------------------------------------------------------------
1 | import { RefObject } from 'react';
2 | import {
3 | StillnessSpec,
4 | StillnessManager,
5 | StillnessContract,
6 | StillnessHandle,
7 | } from '../types';
8 | import { isFunction } from '../utils';
9 |
10 | export class StillnessHandleImpl implements StillnessHandle {
11 | private props: Props | null = null;
12 | private spec: StillnessSpec;
13 | private manager: StillnessManager;
14 | private contract: StillnessContract;
15 |
16 | constructor(
17 | spec: StillnessSpec,
18 | manager: StillnessManager,
19 | contract: StillnessContract
20 | ) {
21 | this.spec = spec;
22 | this.manager = manager;
23 | this.contract = contract;
24 | }
25 |
26 | public receiveProps(props: Props) {
27 | this.props = props;
28 | }
29 |
30 | public mount = () => {
31 | let item;
32 | if (!this.props || !this.spec.mounted || !isFunction(this.spec.mounted)) {
33 | return item;
34 | }
35 |
36 | item = this.spec.mounted(this.props, this.contract);
37 |
38 | return item;
39 | };
40 |
41 | public unmount = () => {
42 | let item;
43 | if (!this.props || !this.spec.unmounted || !isFunction(this.spec.unmounted)) {
44 | return;
45 | }
46 |
47 | item = this.spec.unmounted(this.props, this.contract);
48 |
49 | return item;
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/internals/StillnessRegistrationImpl.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Identifier,
3 | Registration,
4 | StillnessManager,
5 | UniqueId,
6 | Unsubscribe,
7 | RegistrationParams,
8 | } from '../types';
9 | import { getStillnessUniqueId } from '../utils';
10 |
11 | export class StillnessRegistrationImpl implements Registration {
12 | private manager: StillnessManager;
13 | private uniqueId: UniqueId;
14 |
15 | constructor(manager: StillnessManager) {
16 | this.manager = manager;
17 | this.uniqueId = getStillnessUniqueId('uniqueId').toString();
18 | }
19 |
20 | public register: (params: RegistrationParams) => [UniqueId, Unsubscribe] = (
21 | params: RegistrationParams
22 | ) => {
23 | const { createVNode, removeVNode } = this.manager.getActions();
24 | const { parentId, type, visible, isStillness } = params;
25 | createVNode({
26 | uniqueId: this.uniqueId,
27 | parentId,
28 | type,
29 | visible,
30 | isStillness,
31 | });
32 |
33 | return [this.uniqueId, () => removeVNode({ uniqueId: this.uniqueId })];
34 | };
35 |
36 | public update = (params: RegistrationParams) => {
37 | const { parentId, type, visible, isStillness } = params;
38 |
39 | this.manager.getActions().updateVNode({
40 | uniqueId: this.uniqueId,
41 | parentId,
42 | type,
43 | visible,
44 | isStillness,
45 | childQueue: [],
46 | });
47 | };
48 |
49 | public getUniqueId = () => this.uniqueId;
50 | }
51 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/internals/__tests__/StillnessHandleImpl.spec.ts:
--------------------------------------------------------------------------------
1 | import { StillnessHandleImpl } from '../StillnessHandleImpl';
2 | import { StillnessContractImpl } from '../StillnessContractImpl';
3 | import { createStillnessManager } from '../../core/createStillnessManager';
4 |
5 | describe('StillnessHandleImpl', () => {
6 | const spec = {
7 | mounted: (props) => `mounted and count${props?.count || 0}`,
8 | unmounted: (props) => `unmounted and count${props?.count || 0}`,
9 | collect: (props, contract) => {
10 | return {
11 | item: contract.getItem(),
12 | count: props.count,
13 | };
14 | },
15 | };
16 | it('can be constructed', () => {
17 | const manager = createStillnessManager();
18 | const contract = new StillnessContractImpl(manager);
19 |
20 | const handle = new StillnessHandleImpl(spec, manager, contract);
21 |
22 | expect(handle).toBeDefined();
23 | });
24 |
25 | it('Automatically update item when changing props', () => {
26 | let mockManager: any = jest.fn();
27 | let mockContract: any = jest.fn().mockReturnValue({
28 | getItem: () => {
29 | return 'test2';
30 | },
31 | });
32 |
33 | const handle = new StillnessHandleImpl(spec, mockManager, mockContract);
34 |
35 | expect(handle.mount()).toBeUndefined();
36 | expect(handle.unmount()).toBeUndefined();
37 |
38 | handle.receiveProps({ count: 1 } as any);
39 |
40 | const result1 = handle.mount();
41 |
42 | expect(result1).toEqual('mounted and count1');
43 |
44 | handle.receiveProps({ count: 2 } as any);
45 |
46 | const result2 = handle.unmount();
47 |
48 | expect(result2).toEqual('unmounted and count2');
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/internals/__tests__/stillnessRegistrationImpl.spec.ts:
--------------------------------------------------------------------------------
1 | import { StillnessRegistrationImpl } from '../StillnessRegistrationImpl';
2 |
3 | import { createStillnessManager } from '../../core/createStillnessManager';
4 |
5 | describe('StillnessRegistrationImpl', () => {
6 | it('can be constructed', () => {
7 | const manager = createStillnessManager();
8 | const registration = new StillnessRegistrationImpl(manager);
9 | expect(registration).toBeDefined();
10 | });
11 |
12 | it('can register successfully', () => {
13 | const manager = createStillnessManager();
14 | const registration = new StillnessRegistrationImpl(manager);
15 |
16 | const [uniqueId,unRegister] = registration.register({
17 | parentId: 'root',
18 | visible: true,
19 | isStillness: false,
20 | });
21 |
22 | expect(manager.getStore().vNodes[uniqueId]).toBeDefined();
23 |
24 | unRegister();
25 |
26 | expect(manager.getStore().vNodes[uniqueId]).not.toBeDefined();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/internals/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './StillnessContractImpl';
2 | export * from './StillnessHandleImpl';
3 | export * from './StillnessRegistrationImpl';
--------------------------------------------------------------------------------
/packages/react-stillness/src/types/contract.ts:
--------------------------------------------------------------------------------
1 | import { Identifier, UniqueId } from './core';
2 | import { StillnessMonitor } from './monitors';
3 |
4 | export interface ActionsParams {
5 | id?: Identifier;
6 | }
7 |
8 | export interface UnsetParams {
9 | id?: Identifier;
10 | type?: Identifier;
11 | }
12 |
13 | export interface StillnessContract {
14 | getStillnessId(): UniqueId | undefined;
15 |
16 | getStillnessItem(): any;
17 |
18 | getStillnessType(): Identifier | undefined;
19 |
20 | isStillness(): boolean;
21 |
22 | unset(params: UnsetParams): void;
23 |
24 | clear(): void;
25 |
26 | resetMax(max: number): void;
27 | }
28 |
29 | export interface HandlerContract {
30 | receiveId: (id: UniqueId | undefined) => void;
31 | receiveItem: (item: any) => void;
32 | }
33 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/types/handle.ts:
--------------------------------------------------------------------------------
1 | export interface StillnessHandle {
2 | receiveProps(props: any): void;
3 | mount(): any;
4 | unmount(): any;
5 | }
6 |
7 | export interface Handle {
8 | mount(): any;
9 | unmount(): any;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/types/hooks.ts:
--------------------------------------------------------------------------------
1 | import { StillnessContract } from './contract';
2 |
3 | export interface StillnessHookSpec {
4 | /**
5 | * optional
6 | * This event will be triggered after the component goes to rest
7 | * and if there is any return value, it will be returned in contract.getItem()
8 | * @param props
9 | * @param contract
10 | */
11 | mounted?: (contract: StillnessContract) => ResObject | void;
12 | /**
13 | * optional
14 | * This event will be triggered after the component leaves the resting state
15 | * and if there is any return value, it will be returned in contract.getItem()
16 | */
17 | unmounted?: (contract: StillnessContract) => ResObject | void;
18 |
19 | collect?: (contract: StillnessContract) => CollectedProps;
20 | }
21 |
22 | export type FactoryOrInstance = T | (() => T)
23 |
24 | export type StillnessHookCollector = (
25 | contract: StillnessContract
26 | ) => CollectedProps;
27 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './core';
2 | export * from './decorators';
3 | export * from './monitors';
4 | export * from './contract';
5 | export * from './registration';
6 | export * from './handle';
7 | export * from './hooks';
--------------------------------------------------------------------------------
/packages/react-stillness/src/types/monitors.ts:
--------------------------------------------------------------------------------
1 | import { Identifier, UniqueId, Listener, Unsubscribe } from './core';
2 | import { State as VNodeState } from '../core/reducers/vNode';
3 |
4 | export interface StillnessMonitor {
5 | subscribeToStateChange(
6 | listener: Listener,
7 | params: {
8 | parentId?: UniqueId;
9 | }
10 | ): Unsubscribe;
11 |
12 | subscribeToEffectChange(
13 | listener: Listener,
14 | params: {
15 | uniqueId: UniqueId;
16 | type?: Identifier;
17 | }
18 | ): Unsubscribe;
19 |
20 | /**
21 | * Returning true means the component is in the cached state
22 | * The default value is true
23 | */
24 | isStillness(uniqueId: UniqueId | undefined): boolean;
25 |
26 | getStillnessId(uniqueId: UniqueId | undefined): UniqueId | null;
27 |
28 | getStillnessType(uniqueId: UniqueId | undefined): Identifier | undefined;
29 |
30 | getStillnessItem(uniqueId: UniqueId): VNodeState;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/types/registration.ts:
--------------------------------------------------------------------------------
1 | import { Identifier, UniqueId, Unsubscribe } from './core';
2 | import { StillnessMonitor } from './monitors';
3 |
4 | export interface RegistrationParams {
5 | parentId?: UniqueId;
6 | type?: Identifier;
7 | visible?: boolean;
8 | isStillness?: boolean;
9 | }
10 | export interface Registration {
11 | register: (params: RegistrationParams) => [UniqueId, Unsubscribe];
12 | update: (params: RegistrationParams) => void;
13 | getUniqueId: () => UniqueId;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/combineFuncs.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | export const combineFuncs = (slices, ...args) => {
3 | return Object.keys(slices).reduce(
4 | (acc, prop) => ({
5 | ...acc,
6 | [prop]: slices[prop](...args),
7 | }),
8 | {}
9 | );
10 | };
11 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/dom.ts:
--------------------------------------------------------------------------------
1 | export function createWrapperElement() {
2 | const wrapper = document.createElement('div');
3 | wrapper.dataset.type = 'offscreen-wrapper';
4 | return wrapper;
5 | }
6 |
7 | export function isRealChildNode(
8 | targetElement: HTMLElement,
9 | element: any
10 | ) {
11 | let isExist = false;
12 |
13 | if (targetElement.hasChildNodes()) {
14 | for (let node of targetElement.children) {
15 | if (node === element) {
16 | return true;
17 | }
18 | }
19 | }
20 | return isExist;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/getNextUniqueId.ts:
--------------------------------------------------------------------------------
1 | const idCounter = {};
2 |
3 | export function getStillnessUniqueId(prefix: string) {
4 | if (!idCounter[prefix]) {
5 | idCounter[prefix] = 0;
6 | }
7 |
8 | const id = idCounter[prefix]++;
9 |
10 | return `__stillness-${prefix}${id}__`;
11 | }
12 |
13 | export function getNowTimeStr(): string {
14 | return new Date().getTime().toString();
15 | }
16 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/getNodes.ts:
--------------------------------------------------------------------------------
1 | import { Identifier, UniqueId } from '../types';
2 | import { isUndefined } from './is';
3 |
4 | interface Params {
5 | nodes: any;
6 | id?: UniqueId;
7 | type?: Identifier;
8 | }
9 |
10 | export function getNodeIdsByCondition({ nodes, id, type }: Params): UniqueId[] {
11 | if (!id) {
12 | return [];
13 | }
14 | let result = [id, ...getNodeIdsByRootId(nodes, id)];
15 |
16 | if (!isUndefined(type)) {
17 | for (let k of Object.keys(nodes)) {
18 | const _id = nodes[k].uniqueId;
19 | if (nodes[k].type === type && !result.includes(_id)) {
20 | let arr = [_id, ...getNodeIdsByRootId(nodes, _id)];
21 | result = [...result, ...arr];
22 | }
23 | }
24 | }
25 |
26 | return result;
27 | }
28 |
29 | export function getNodeIdsByRootId(nodes: any, rootId: UniqueId): UniqueId[] {
30 | let result = [];
31 |
32 | let queue = [];
33 |
34 | for (let k of Object.keys(nodes)) {
35 | if (nodes[k].parentId === rootId) {
36 | queue.push(nodes[k].uniqueId);
37 | }
38 | }
39 |
40 | while (queue.length > 0) {
41 | var v = queue.shift();
42 | result.push(v);
43 | for (let w of Object.keys(nodes)) {
44 | if (nodes[w].parentId === v) {
45 | queue.push(nodes[w].uniqueId);
46 | }
47 | }
48 | }
49 |
50 | return result;
51 | }
52 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './getNextUniqueId';
2 | export * from './shallowEqual';
3 | export * from './is';
4 | export * from './dom';
5 | export * from './combineFuncs';
6 | export * from './throttle';
7 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/shallowEqual.ts:
--------------------------------------------------------------------------------
1 | export function shallowEqual(
2 | objA: T,
3 | objB: T,
4 | compare?: (a: T, b: T, key?: string) => boolean | void,
5 | compareContext?: any
6 | ) {
7 | var compareResult = compare
8 | ? compare.call(compareContext, objA, objB)
9 | : void 0;
10 | if (compareResult !== void 0) {
11 | return !!compareResult;
12 | }
13 |
14 | if (objA === objB) {
15 | return true;
16 | }
17 |
18 | if (typeof objA !== 'object' || !objA || typeof objB !== 'object' || !objB) {
19 | return false;
20 | }
21 |
22 | var keysA = Object.keys(objA);
23 | var keysB = Object.keys(objB);
24 |
25 | if (keysA.length !== keysB.length) {
26 | return false;
27 | }
28 |
29 | var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
30 |
31 | // Test for A's keys different from B.
32 | for (var idx = 0; idx < keysA.length; idx++) {
33 | var key = keysA[idx];
34 |
35 | if (!bHasOwnProperty(key)) {
36 | return false;
37 | }
38 |
39 | var valueA = (objA as any)[key];
40 | var valueB = (objB as any)[key];
41 |
42 | compareResult = compare
43 | ? compare.call(compareContext, valueA, valueB, key)
44 | : void 0;
45 |
46 | if (
47 | compareResult === false ||
48 | (compareResult === void 0 && valueA !== valueB)
49 | ) {
50 | return false;
51 | }
52 | }
53 |
54 | return true;
55 | }
56 |
--------------------------------------------------------------------------------
/packages/react-stillness/src/utils/throttle.ts:
--------------------------------------------------------------------------------
1 | const now = Date.now || function() {
2 | return new Date().getTime();
3 | };
4 |
5 | export const throttle = (
6 | func: any,
7 | context: any,
8 | wait: number,
9 | options?: { trailing?: boolean; leading?: boolean }
10 | ) => {
11 | let timeout: any, args: any, result: any;
12 |
13 | let previous = 0;
14 |
15 | var later = function () {
16 | previous = options?.leading === false ? 0 : now();
17 | timeout = null;
18 | result = func.apply(context, args);
19 | if (!timeout) context = args = null;
20 | };
21 |
22 | let throttled: any = (...params: any[]) => {
23 | var _now = now();
24 | if (!previous && options?.leading === false) previous = _now;
25 | var remaining = wait - (_now - previous);
26 | context = this;
27 | args = params;
28 | if (remaining <= 0 || remaining > wait) {
29 | if (timeout) {
30 | clearTimeout(timeout);
31 | timeout = null;
32 | }
33 | previous = _now;
34 | result = func.apply(context, args);
35 | if (!timeout) context = args = null;
36 | } else if (!timeout && options?.trailing !== false) {
37 | timeout = setTimeout(later, remaining);
38 | }
39 | return result;
40 | };
41 |
42 | throttled.cancel = function () {
43 | clearTimeout(timeout);
44 | previous = 0;
45 | timeout = context = args = null;
46 | };
47 |
48 | return throttled;
49 | };
50 |
--------------------------------------------------------------------------------
/packages/react-stillness/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "moduleResolution": "node",
5 | "esModuleInterop": true,
6 | "jsx": "react",
7 | "allowJs": true,
8 | "experimentalDecorators": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noImplicitReturns": false,
11 | "suppressImplicitAnyIndexErrors": true,
12 | "declaration": true,
13 | "declarationDir": ".",
14 | "baseUrl": ".",
15 | "target": "ESNext",
16 | "module": "ESNext",
17 | "preserveSymlinks": true,
18 | "allowSyntheticDefaultImports": true,
19 | "strictPropertyInitialization": false
20 | },
21 | "include": ["src/**/*", "./jest-setup.ts"],
22 | "exclude": [
23 | "node_modules",
24 | "**/dist",
25 | "**/es",
26 | "example",
27 | "**/*.test.ts",
28 | "**/*.test.tsx",
29 | "**/*.spec.ts",
30 | "fixtures",
31 | "**/lib",
32 | "**/scripts"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/packages/umi-plugin-stillness/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "umi-plugin-stillness",
3 | "version": "0.2.8",
4 | "description": "keep-alive for umi base on react-stillness-component",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "clean": "rimraf dist"
8 | },
9 | "files": [
10 | "dist"
11 | ],
12 | "keywords": [
13 | "keep-alive",
14 | "stillness",
15 | "react-stillness-component"
16 | ],
17 | "peerDependencies": {
18 | "umi": "3.x"
19 | },
20 | "author": "leomyili",
21 | "license": "MIT",
22 | "dependencies": {
23 | "@umijs/types": "^3.5.23",
24 | "react-stillness-component": "^0.9.1",
25 | "umi-renderer-stillness": "^0.2.8"
26 | },
27 | "rollup": {
28 | "input": "src/index.ts",
29 | "output": [
30 | {
31 | "file": "dist/index.js",
32 | "format": "umd",
33 | "name": "StillnessUmiPlugin"
34 | }
35 | ],
36 | "external": []
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/umi-plugin-stillness/src/assets/exports.tpl:
--------------------------------------------------------------------------------
1 | export {
2 | useStillness,
3 | useStillnessManager,
4 | Offscreen,
5 | connectStillness,
6 | StillnessProvider,
7 | INSTANCE_SYM,
8 | } from 'react-stillness-component';
9 | export { IRoute as StillnessIRoute } from "umi-renderer-stillness";
10 |
--------------------------------------------------------------------------------
/packages/umi-plugin-stillness/src/index.ts:
--------------------------------------------------------------------------------
1 | import { IApi } from '@umijs/types';
2 | import { dirname, join } from 'path';
3 | import { readFileSync } from 'fs';
4 |
5 | export default (api: IApi) => {
6 | api.describe({
7 | key: 'stillness',
8 | config: {
9 | schema(joi) {
10 | return joi.object();
11 | },
12 | },
13 | enableBy: api.EnableBy.config,
14 | });
15 |
16 | api.modifyRendererPath(() => {
17 | return dirname(require.resolve('umi-renderer-stillness/package.json'));
18 | });
19 |
20 | api.onGenerateFiles({
21 | fn() {
22 | const exportsTpl = readFileSync(
23 | join(__dirname, 'assets/exports.tpl'),
24 | 'utf-8'
25 | );
26 |
27 | api.writeTmpFile({
28 | path: 'plugin-stillness/exports.ts',
29 | content: exportsTpl,
30 | });
31 | },
32 | });
33 |
34 | api.addUmiExports(() => [
35 | {
36 | exportAll: true,
37 | source: '../plugin-stillness/exports',
38 | },
39 | ]);
40 | };
41 |
--------------------------------------------------------------------------------
/packages/umi-plugin-stillness/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "moduleResolution": "node",
5 | "esModuleInterop": true,
6 | "jsx": "react",
7 | "allowJs": true,
8 | "experimentalDecorators": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noImplicitReturns": false,
11 | "suppressImplicitAnyIndexErrors": true,
12 | "declaration": true,
13 | "declarationDir": ".",
14 | "baseUrl": ".",
15 | "target": "ESNext",
16 | "module": "ESNext",
17 | "typeRoots": ["./node_modules/@types"],
18 | "preserveSymlinks": true,
19 | "allowSyntheticDefaultImports": true,
20 | "strictPropertyInitialization": false
21 | },
22 | "include": ["src/**/*"],
23 | "exclude": [
24 | "node_modules",
25 | "**/dist",
26 | "**/es",
27 | "example",
28 | "**/*.spec.ts",
29 | "fixtures",
30 | "**/lib",
31 | "**/scripts"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "umi-renderer-stillness",
3 | "version": "0.2.8",
4 | "description": "react-stillness-component renderer",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "clean": "rimraf dist"
8 | },
9 | "keywords": [
10 | "keep-alive",
11 | "stillness",
12 | "react-stillness-component"
13 | ],
14 | "files": [
15 | "dist"
16 | ],
17 | "author": "leomyili",
18 | "license": "MIT",
19 | "dependencies": {
20 | "@types/react": "^17.x",
21 | "@types/react-dom": "^17.x",
22 | "@types/react-router-config": "^5.0.2",
23 | "@umijs/runtime": "^3.x",
24 | "react-router-config": "5.1.1",
25 | "react-stillness-component": "^0.9.1"
26 | },
27 | "peerDependencies": {
28 | "react": "16.x || 17.x",
29 | "react-dom": "16.x || 17.x"
30 | },
31 | "rollup": {
32 | "input": "src/index.ts",
33 | "output": [
34 | {
35 | "file": "dist/index.js",
36 | "format": "umd",
37 | "name": "StillnessUmiRenderer",
38 | "globals": {
39 | "react": "React",
40 | "react-dom": "ReactDOM",
41 | "runtime": "runtime",
42 | "react-stillness-component": "reactStillnessComponent"
43 | }
44 | }
45 | ],
46 | "external": [
47 | "react",
48 | "react-dom",
49 | "@umijs/runtime",
50 | "react-stillness-component"
51 | ]
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/src/index.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
3 | declare global {
4 | interface Window {
5 | g_useSSR: any;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/src/index.ts:
--------------------------------------------------------------------------------
1 | import { FunctionComponent } from 'react';
2 | import { History, Location } from 'history-with-query';
3 | import { match } from 'react-router-dom';
4 |
5 | export interface IComponent extends FunctionComponent {
6 | getInitialProps?: Function;
7 | preload?: () => Promise;
8 | }
9 |
10 | export interface IRoute {
11 | path?: string;
12 | exact?: boolean;
13 | redirect?: string;
14 | component?: IComponent;
15 | routes?: IRoute[];
16 | key?: any;
17 | strict?: boolean;
18 | sensitive?: boolean;
19 | wrappers?: any[];
20 | stillness?: any[];
21 | [k: string]: any;
22 | }
23 |
24 | export interface IRouteComponentProps<
25 | Params extends { [K in keyof Params]?: string } = {},
26 | Query extends { [K in keyof Query]?: string } = {}
27 | > {
28 | children: JSX.Element;
29 | location: Location & { query: Query };
30 | route: IRoute;
31 | history: History;
32 | match: match;
33 | }
34 |
35 | export { default as renderClient } from './renderClient/renderClient';
36 | export { default as renderRoutes } from './renderRoutes/renderRoutes';
37 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/src/renderRoutes/Route.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { __RouterContext as RouterContext } from '@umijs/runtime';
3 |
4 | export default function Route(props: any) {
5 | return (
6 |
7 | {(context: any) => {
8 | const { location } = context;
9 | const match = props.computedMatch;
10 | const newProps = { ...context, location, match };
11 | let { render, stillness } = props;
12 |
13 | return (
14 |
15 | {newProps.match || stillness
16 | ? render({
17 | ...props.layoutProps,
18 | ...newProps,
19 | })
20 | : null}
21 |
22 | );
23 | }}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/src/renderRoutes/StillnessSwitch.tsx:
--------------------------------------------------------------------------------
1 | import React, { isValidElement, Children, cloneElement } from 'react';
2 | import { __RouterContext as RouterContext, matchPath } from '@umijs/runtime';
3 | import { Offscreen } from 'react-stillness-component';
4 |
5 | export default function Switch(props: any) {
6 | return (
7 |
8 | {(context: any) => {
9 | const { children, ...extraProps } = props;
10 | const { match } = context;
11 |
12 | const location = props.location || context.location;
13 | let element: any,
14 | isExist: boolean = false;
15 |
16 | return Children.map(
17 | children,
18 | (child: {
19 | props: { path: string; from: string; stillness: boolean };
20 | }) => {
21 | if (isValidElement(child)) {
22 | const path = child.props.path || child.props.from;
23 | element = child;
24 |
25 | const childMatch = isExist
26 | ? null
27 | : path
28 | ? matchPath(location.pathname, { ...child.props, path })
29 | : match;
30 |
31 | if (childMatch !== null) {
32 | isExist = true;
33 | }
34 |
35 | if (child.props.stillness) {
36 | return (
37 |
38 | {cloneElement(element, {
39 | location,
40 | computedMatch: childMatch,
41 | layoutProps: extraProps,
42 | })}
43 |
44 | );
45 | } else if (childMatch) {
46 | return cloneElement(element, {
47 | location,
48 | computedMatch: childMatch,
49 | layoutProps: extraProps,
50 | });
51 | }
52 | }
53 |
54 | return null;
55 | }
56 | );
57 | }}
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "moduleResolution": "node",
5 | "esModuleInterop": true,
6 | "jsx": "react",
7 | "allowJs": true,
8 | "experimentalDecorators": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noImplicitReturns": false,
11 | "suppressImplicitAnyIndexErrors": true,
12 | "declaration": true,
13 | "declarationDir": ".",
14 | "baseUrl": ".",
15 | "target": "ESNext",
16 | "module": "ESNext",
17 | "typeRoots": ["./node_modules/@types","./src/index.d.ts"],
18 | "preserveSymlinks": true,
19 | "allowSyntheticDefaultImports": true,
20 | "strictPropertyInitialization": false
21 | },
22 | "include": ["src/**/*"],
23 | "exclude": [
24 | "node_modules",
25 | "**/dist",
26 | "**/es",
27 | "example",
28 | "**/*.spec.ts",
29 | "fixtures",
30 | "**/lib",
31 | "**/scripts"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/packages/umi-renderer-stillness/types.d.ts:
--------------------------------------------------------------------------------
1 | interface Window {
2 | g_useSSR: any;
3 | }
--------------------------------------------------------------------------------
/scripts/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | while [[ "$#" > 0 ]]; do case $1 in
4 | -r|--release) release="$2"; shift;;
5 | -b|--branch) branch="$2"; shift;;
6 | *) echo "Unknown parameter passed: $1"; exit 1;;
7 | esac; shift; done
8 |
9 | # Default as minor, the argument major, minor or patch:
10 | if [ -z "$release" ]; then
11 | release="minor";
12 | fi
13 |
14 | # Default release branch is master
15 | if [ -z "$branch" ] ; then
16 | branch="master";
17 | fi;
18 |
19 |
20 | echo "Branch is $branch"
21 | echo "Release as $release"
22 |
23 | # Tag prefix
24 | prefix="prefix_v"
25 |
26 | git pull origin $branch
27 | echo "Current pull origin $branch."
28 |
29 | # Generate version number and tag
30 | standard-version -r $release --tag-prefix $prefix --infile CHANGELOG.md
31 |
32 | git push --follow-tags origin $branch
33 |
34 | echo "Release finished."
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "moduleResolution": "node",
5 | "esModuleInterop": true,
6 | "jsx": "react",
7 | "allowJs": true,
8 | "experimentalDecorators": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noImplicitReturns": false,
11 | "suppressImplicitAnyIndexErrors": true,
12 | "declaration": true,
13 | "declarationDir": ".",
14 | "baseUrl": ".",
15 | "target": "ESNext",
16 | "module": "ESNext",
17 | "typeRoots": ["./node_modules/@types"],
18 | "preserveSymlinks": true,
19 | "allowSyntheticDefaultImports": true,
20 | "strictPropertyInitialization": false
21 | },
22 | "include": ["packages/**/*"],
23 | "exclude": [
24 | "node_modules",
25 | "**/dist",
26 | "**/es",
27 | "example",
28 | "**/*.spec.ts",
29 | "fixtures",
30 | "**/lib",
31 | "**/scripts"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42 |
--------------------------------------------------------------------------------
/website/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/website/blog/BDD Test.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 用BDD的思想做组件测试
3 | description: 这是我关于前端自动化测试的第二篇博文。
4 | date: 2022-07-31T13:30
5 | slug: bdd-test
6 | authors: leomYili
7 | tags: [BDD, test]
8 | hide_table_of_contents: false
9 | ---
10 |
11 |
--------------------------------------------------------------------------------
/website/blog/assets/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/1.png
--------------------------------------------------------------------------------
/website/blog/assets/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/10.png
--------------------------------------------------------------------------------
/website/blog/assets/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/11.png
--------------------------------------------------------------------------------
/website/blog/assets/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/12.png
--------------------------------------------------------------------------------
/website/blog/assets/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/13.png
--------------------------------------------------------------------------------
/website/blog/assets/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/2.png
--------------------------------------------------------------------------------
/website/blog/assets/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/3.png
--------------------------------------------------------------------------------
/website/blog/assets/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/4.png
--------------------------------------------------------------------------------
/website/blog/assets/5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/5.gif
--------------------------------------------------------------------------------
/website/blog/assets/6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/6.gif
--------------------------------------------------------------------------------
/website/blog/assets/7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/7.gif
--------------------------------------------------------------------------------
/website/blog/assets/8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/8.gif
--------------------------------------------------------------------------------
/website/blog/assets/9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leomYili/react-stillness-component/99b82ff516a8f28cdd57a4cd5927484d6a8ac939/website/blog/assets/9.gif
--------------------------------------------------------------------------------
/website/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/docs/api/Components/Offscreen.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | id: offscreen
4 | title: Offscreen
5 | ---
6 |
7 | > Wrapping components that need to be stationary with ``
8 |
9 | ## usage
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 | setShow((show) => !show)}>Toggle
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 | setShow((show) => !show)}>Toggle
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 |
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 |
--------------------------------------------------------------------------------