├── .editorconfig
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .travis.yml
├── .yarnrc
├── CNAME
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs
├── .nojekyll
├── CNAME
├── _coverpage.md
├── _sidebar.md
├── advanced.md
├── faq.md
├── get-started.md
├── googlecc31d09edfe4e7cb.html
├── guide.md
├── index.html
├── third-party-libraries.md
└── zh-cn
│ ├── _coverpage.md
│ ├── _sidebar.md
│ ├── advanced.md
│ ├── faq.md
│ ├── get-started.md
│ ├── guide.md
│ └── third-party-libraries.md
├── gulpfile.js
├── jest.config.js
├── now.json
├── package.json
├── rollup.config.js
├── src
├── __tests__
│ ├── __snapshots__
│ │ └── store.test.tsx.snap
│ ├── store.test.tsx
│ ├── stores
│ │ └── foo.store.ts
│ └── utils.ts
├── consumer.tsx
├── container.tsx
├── error.ts
├── executor.tsx
├── index.ts
├── provider.tsx
└── store.ts
├── test.html
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Node CI Test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | strategy:
11 | matrix:
12 | node-version: [8.x, 10.x, 12.x]
13 |
14 | steps:
15 | - uses: actions/checkout@v1
16 | - name: Use Node.js ${{ matrix.node-version }}
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | - name: Install yarn
21 | run: |
22 | curl -o- -L https://yarnpkg.com/install.sh | bash
23 | - name: Yarn install, test, and build
24 | run: |
25 | yarn install
26 | yarn test
27 | yarn build
28 | env:
29 | CI: true
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /lib/
2 | .DS_Store
3 |
4 | # IDE
5 | .idea/
6 |
7 | # Logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # TypeScript v1 declaration files
46 | typings/
47 |
48 | # Optional npm cache directory
49 | .npm
50 |
51 | # Optional eslint cache
52 | .eslintcache
53 |
54 | # Optional REPL history
55 | .node_repl_history
56 |
57 | # Output of 'npm pack'
58 | *.tgz
59 |
60 | # Yarn Integrity file
61 | .yarn-integrity
62 |
63 | # dotenv environment variables file
64 | .env
65 |
66 | # next.js build output
67 | .next
68 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "lts/*"
4 | before_install:
5 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.17.3
6 | - export PATH="$HOME/.yarn/bin:$PATH"
7 | install:
8 | - yarn install
9 | - yarn global add codecov codacy-coverage
10 | script:
11 | - yarn test --coverage
12 | - yarn build
13 | after_success:
14 | - codecov
15 | - cat ./coverage/lcov.info | codacy-coverage
16 | cache:
17 | yarn: true
18 | directories:
19 | - "node_modules"
20 | notifications:
21 | email:
22 | on_success: never
23 | on_failure: always
24 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | registry "https://registry.npmjs.org"
2 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | reto.js.org
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at awmleer@sparker.xyz. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 awmleer
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 | # Reto
2 |
3 | [](https://github.com/awmleer/reto)
4 | [](https://codecov.io/gh/awmleer/reto)
5 | [](https://www.npmjs.com/package/reto)
6 | [](https://www.npmjs.com/package/reto)
7 | [](https://www.npmjs.com/package/reto)
8 | [](https://travis-ci.org/awmleer/reto)
9 | [](https://app.codacy.com/app/awmleer/reto)
10 | [](https://app.snyk.io/test/github/awmleer/reto?targetFile=package.json)
11 | 
12 |
13 | ```
14 | ___ __
15 | / _ \___ ___ _____/ /_
16 | / , _/ -_) / _ `/ __/ __/
17 | ____ /_/|_|\__/ \_,_/\__/\__/
18 | / __/ / /____ _______
19 | _\ \ / __/ _ \ / __/ -_)
20 | /___/ \__/\___/ /_/ \__/
21 |
22 | ```
23 |
24 | Flexible and efficient React Store with hooks.
25 |
26 | ## Features
27 |
28 | - Supports all react hooks. Writing a store is just like writing a component.
29 | - Simple but efficient, quite easy to learn.
30 | - Use multiple stores to organize your data.
31 | - Dependency injection based on React Context.
32 | - Strongly typed with Typescript, also works well with JS.
33 |
34 | ## Docs
35 |
36 | [English](https://reto.js.org/#/) | [中文](https://reto.js.org/#/zh-cn/)
37 |
38 | ## Install
39 |
40 | ```bash
41 | $ yarn add reto
42 | # or
43 | $ npm install reto --save
44 | ```
45 |
46 | ## Try It Online
47 |
48 | [](https://codesandbox.io/s/github/awmleer/reto-demo/tree/master/?fontsize=14)
49 |
50 | ## A Simple Example
51 |
52 | Every `Store` is a function similar to a custom hook. In the body of a `Store` function, you can use any react hooks, for example, `useState`, `useEffect`, `useRef`.
53 |
54 | ```jsx
55 | export function FooStore() {
56 | const [x, setX] = useState(initial)
57 | return {
58 | x,
59 | setX
60 | }
61 | }
62 | ```
63 |
64 | Then, you can provide a store "instance" using `Provider` component.
65 |
66 | ```jsx
67 | import {Provider} from 'reto'
68 |
69 |
70 |
71 |
72 | ```
73 |
74 | By using the `useStore` hook, you can retrieve the store "instance" in components, and subscribe to its changes.
75 |
76 | ```jsx
77 | import {useStore} from 'reto'
78 |
79 | const App: FC = (props) => {
80 | const fooStore = useStore(FooStore)
81 |
82 | function changeStore() {
83 | fooStore.setX(fooStore.x + 1)
84 | }
85 | return (
86 |
87 |
88 | {fooStore.x}
89 |
90 | )
91 | }
92 | ```
93 |
94 | So when you click the "Change" button, the `setX` function of `fooStore` is executed, thereby triggers the update of state `x` and the rerender of `App` component. Everything is simple and straightforward.
95 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awmleer/reto/c5ae198faead0b1ad1ed66418d62cc7242475026/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | reto.js.org
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 | # Reto
2 |
3 | > Flexible and efficient React store with hooks.
4 |
5 | - Writing a store is just like writing a custom hook.
6 | - Simple but efficient, learn in 10 minutes.
7 |
8 | [GitHub](https://github.com/awmleer/reto/)
9 | [中文](/zh-cn/)
10 | [Get Started](/get-started)
11 |
12 | 
13 |
14 | 
15 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | * [Get Started](./get-started.md)
2 | * [Guide](./guide.md)
3 | * [Advanced](./advanced.md)
4 | * [FAQ](./faq.md)
5 | * [Third Party Libraries](./third-party-libraries.md)
6 |
--------------------------------------------------------------------------------
/docs/advanced.md:
--------------------------------------------------------------------------------
1 | ## Pass Arguments to Store
2 |
3 | You can use the `args` prop of `Provider` to pass arguments to Store.
4 |
5 | ```jsx
6 | export function FooStore(initial = 1) {
7 | const [x, setX] = useState(initial)
8 | return {
9 | x,
10 | setX
11 | }
12 | }
13 | ```
14 |
15 | ```jsx
16 |
17 |
18 |
19 | ```
20 |
21 | ## Use in Class Components
22 |
23 | Even though Reto itself is written with hooks, it is still supported to use Reto in class components. You may wonder how to use `useStore` in class components. The answer is: No, you can't. But, there is an substitute for `useStore`, which is `Consumer` component:
24 |
25 | ```jsx
26 | import {Consumer} from 'reto'
27 |
28 | export class App extends Component {
29 | render() {
30 | return (
31 |
32 | {fooStore => (
33 | fooStore.x
34 | )}
35 |
36 | )
37 | }
38 | }
39 | ```
40 |
41 |
42 | ## How to Solve the Performance Issue
43 |
44 | There are several ways to solve the performance issuse:
45 |
46 | ### Eliminate Redundant Updates of Provider
47 |
48 | When the parent component re-renders, the `Provider` component in it will also re-render, therefore the Store Hook will be re-executed. And finally, all the components subscribed to this Store will be re-rendered. This kind of cascading update will often result in dramatically performance cost.
49 |
50 | ```jsx
51 | function App(props) {
52 | return (
53 |
54 | {props.children}
55 |
56 | )
57 | }
58 | ```
59 |
60 | Fortunately, reto provides the `memo` props for `Provider`, which can be enabled to avoid redundant updates of `Provider`.
61 |
62 | ```jsx
63 | function App(props) {
64 | return (
65 |
66 | {props.children}
67 |
68 | )
69 | }
70 | ```
71 |
72 | When `memo` is turned on, `Provider` will perform a shallow comparison of the old and new `args` arrays. If there is no change, this update will be skipped.
73 |
74 | > In most cases, we recommend turning on `memo`, which is very straightforward and effective for performance optimization.
75 |
76 | ### Partial Subscription
77 |
78 | If a store is too big or it updates too frequently, there may be a performance issue.
79 |
80 | You can pass an additional `deps` function to `useStore` for controlling whether to rerender.
81 |
82 | ```jsx
83 | const fooStore = useStore(FooStore, store => [store.x, store.y[0]])
84 | ```
85 |
86 | This is very similar to `useMemo` and `useEffect`. But please notice that the `deps` of `useStore` is **function**.
87 |
88 | ### Split Store
89 |
90 | We recommend splitting a large Store into small parts, so that not only is the code easier to maintain, but performance can also get improved.
91 |
92 | ## Store Ref
93 |
94 | If you want to get the ref of store object, you can use the `storeRef` prop of `Provider`:
95 |
96 | ```jsx
97 | const storeRef = useRef()
98 | function increase() {
99 | storeRef.current.setCount(count + 1)
100 | }
101 | return (
102 |
103 | {/*...*/}
104 |
105 | )
106 | ```
107 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | ## Is it another "redux"?
2 |
3 | No. Reto is the replacement for redux and redux-saga. It does the same thing as they do (building app's model layer), but in a more flexible and efficient way.
4 |
5 | ## Can I use Reto in production?
6 |
7 | Yes. Currently we are using Reto in a product at Alibaba.
8 |
9 | ## Will Reto increase my bundle size significantly?
10 |
11 | No. Reto is very very simple and small.
12 |
13 | ## Can I use Reto in a TypeScript project?
14 |
15 | Of course. Reto itself is written in TypeScript.
16 |
17 | ## Can I use Reto with React 16.7 or lower?
18 |
19 | Sorry you can't. Reto is based on the "hooks" feature which is introduced in React 16.8. So if you want to use Reto, you need to upgrade React to 16.8 or higher.
20 |
21 | ## I don't write function components, can I use Reto?
22 |
23 | Yes. Reto is compatible with class components. You can only use hooks in your stores.
24 |
--------------------------------------------------------------------------------
/docs/get-started.md:
--------------------------------------------------------------------------------
1 | > **Flexible** and **efficient** React store with hooks.
2 |
3 | ```
4 | ___ __
5 | / _ \___ ___ _____/ /_
6 | / , _/ -_) / _ `/ __/ __/
7 | ____ /_/|_|\__/ \_,_/\__/\__/
8 | / __/ / /____ _______
9 | _\ \ / __/ _ \ / __/ -_)
10 | /___/ \__/\___/ /_/ \__/
11 |
12 | ```
13 |
14 | ## Features
15 |
16 | - Supports all React hooks. Writing a store is just like writing a custom hook.
17 | - Simple but efficient, learn in 10 minutes.
18 | - Use multiple stores to organize your data.
19 | - Dependency injection based on React Context.
20 | - Strongly typed with Typescript, also works well with JS.
21 |
22 | ## Try It Online
23 |
24 |
25 |
26 | ## FAQ
27 |
28 | [Here](faq.md) are some frequently asked questions.
29 |
30 | ## Install
31 |
32 | ```bash
33 | $ yarn add reto
34 | # or
35 | $ npm install reto --save
36 | ```
37 |
38 | ## A Simple Example
39 |
40 | Every `Store` is a function similar to a custom hook. In the body of a `Store` function, you can use any react hooks, for example, `useState`, `useEffect`, `useRef`.
41 |
42 | ```jsx
43 | export function CounterStore() {
44 | const [count, setCount] = useState(1)
45 |
46 | useEffect(() => {
47 | console.log('x is updated.')
48 | }, [count])
49 |
50 | function increase() {
51 | setCount(count + 1)
52 | }
53 |
54 | return {
55 | count,
56 | increase,
57 | }
58 | }
59 | ```
60 |
61 | Then, you can provide a store "instance" using the `Provider` component.
62 |
63 | ```jsx
64 | import {Provider} from 'reto'
65 |
66 |
67 |
68 |
69 | ```
70 |
71 | By using the `useStore` hook, you can retrieve the store "instance" in components, and subscribe to its changes.
72 |
73 | ```jsx
74 | import {useStore} from 'reto'
75 |
76 | const App: FC = (props) => {
77 | const counterStore = useStore(CounterStore)
78 |
79 | return (
80 |
81 |
82 | {counterStore.count}
83 |
84 | )
85 | }
86 | ```
87 |
88 | So when you click the "Change" button, the `setX` function of `fooStore` is executed, thereby triggers the update of state `x` and the rerender of `App` component. Everything is simple and straightforward.
89 |
--------------------------------------------------------------------------------
/docs/googlecc31d09edfe4e7cb.html:
--------------------------------------------------------------------------------
1 | google-site-verification: googlecc31d09edfe4e7cb.html
--------------------------------------------------------------------------------
/docs/guide.md:
--------------------------------------------------------------------------------
1 | ## Create a Store
2 |
3 | In reto, a Store is essentially a **function**, and we can call any react hooks in it:
4 |
5 | ```jsx
6 | export function CounterStore() {
7 | const [count, setCount] = useState(1)
8 |
9 | useEffect(() => {
10 | console.log('x is updated.')
11 | }, [count])
12 |
13 | function increase() {
14 | setCount(count + 1)
15 | }
16 |
17 | return {
18 | count,
19 | increase,
20 | }
21 | }
22 | ```
23 |
24 | Theoretically, store can return a value of any type. However, we recommend **wrap data and operation functions into an object**, and then return this object.
25 |
26 | You'll be quite familiar with the function above if you have written a [custom hook](https://reactjs.org/docs/hooks-custom.html) before. If we change the name of this function into `useCounter`, then it'll just be a custom hook.
27 |
28 | There is another way to understand it: You can regard a store as a special react component. While there are some difference between a store function and a react function component: 1. No props. 2. The return value is not a react node.
29 |
30 | ## Provide a Store Instance
31 |
32 | You can provide a store "instance" using the `Provider` component.
33 |
34 | > To be precise, a store is a function, so there is no "instance". Describing in this way is just for ease of understanding.
35 |
36 | ```jsx
37 | import {Provider} from 'reto'
38 |
39 |
40 |
41 |
42 | ```
43 |
44 | Every `Provider` component creates a standalone "**sandbox environment**", and execute the store function in this sandbox.
45 |
46 | ## Get the Store Instance
47 |
48 |
49 | By using the `useStore` hook, you can retrieve the store "instance" in components, and **subscribe** to its changes.
50 |
51 | ```jsx
52 | import {useStore} from 'reto'
53 |
54 | const App: FC = (props) => {
55 | const counterStore = useStore(CounterStore)
56 |
57 | return (
58 |
59 |
60 | {counterStore.count}
61 |
62 | )
63 | }
64 | ```
65 |
66 | > Actually, `useStore` is `useContext` under the hood.
67 |
68 | ## Hierarchical Store Tree
69 |
70 | Reto uses React's Context API under the hood. Thereby, you can create a **hierarchical** store tree.
71 |
72 | ```
73 | ─ Provider of FooStore -> mark as instance A
74 | ├─ Cosumer of FooStore -> we got instance A
75 | └─ Provider of FooStore -> mark as instance B
76 | └─ Consumer of FooStore -> we got instance B
77 | ```
78 |
79 | Each of the two `Provider`s provides an instance of `FooStore`, and the data are **isolated** completely.
80 |
81 | When a component consumes this Store, it will fetch an instance from the nearest `Provider`.
82 |
83 | ## Dependencies Between Stores
84 |
85 | If you want to "inject" `FooStore` store into `BarStore`, you can call `useStore` in the body of `BarStore`.
86 |
87 | ```jsx
88 | export function BarStore() {
89 | const fooStore = useStore(FooStore)
90 | const [x, setX] = useState(2)
91 | return {
92 | x,
93 | setX,
94 | fooStore,
95 | }
96 | }
97 | ```
98 |
99 | In the mean while, Reto will know `FooStore` is the dependency of BarStore. So whenever `FooStore` updates, `BarStore` will update too.
100 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Reto - Flexible and efficient React store with hooks.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/docs/third-party-libraries.md:
--------------------------------------------------------------------------------
1 | ## useMemo
2 |
3 | If you are familiar with vue's `computed` feature, `useMemo` can help you to solve similar problems.
4 |
5 | ## useAsyncMemo
6 |
7 | [useAsyncMemo](https://github.com/awmleer/use-async-memo) is very suitable for handling asynchronous computed data, for example, fetching paginated data or calling search API when input changes.
8 |
9 | ## withWrapper
10 |
11 | If you create a `Provider` in one component, then you can't get the Store instance with `useStore` in this component:
12 |
13 | ```jsx
14 | const App = function() {
15 | const fooStore = useStore(FooStore) // You can't get the FooStore instance here
16 | return (
17 |
18 |
Hello.
19 |
20 | )
21 | }
22 | ```
23 |
24 | To solve this problem, you can use [withWrapper](https://github.com/awmleer/with-wrapper):
25 |
26 | ```jsx
27 | const App = withWrapper(element => (
28 |
29 | {element}
30 |
31 | ))(function() {
32 | const fooStore = useStore(FooStore) // Now you can get the FooStore instance
33 | return (
34 |
Hello.
35 | )
36 | })
37 | ```
38 |
39 | ## immer
40 |
41 | [immer](https://github.com/immerjs/immer) is a tiny package that allows you to work with immutable state in a more convenient way. You can see [here](https://github.com/immerjs/immer#reactsetstate-example) for more information.
42 |
43 | ## useAction
44 |
45 | the function passed to `useEffect` fires after layout and paint, during a deferred event. If you need to execute the effect function immediately, you can use [useAction](https://github.com/awmleer/use-action) in replacement.
46 |
47 | > Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event. This makes it suitable for the many common side effects, like setting up subscriptions and event handlers, because most types of work shouldn’t block the browser from updating the screen.
48 |
49 | ## useCreation
50 |
51 | [useCreation](https://github.com/awmleer/use-creation) is the replacement for `useMemo` or `useRef`.
52 |
53 | `useMemo` can't guarantee the memoized value will not be recalculated, while `useCreation` can guarantee that.
54 |
55 | > **You may rely on useMemo as a performance optimization, not as a semantic guarantee.**In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without `useMemo` — and then add it to optimize performance.
56 |
57 | And similar to `useRef`, you can use `useCreation` to create some constants. But `useCreation` can avoid performance hazards.
58 |
59 | ```javascript
60 | const a = useRef(new Subject()) //A new Subject instance is created in every render.
61 | const b = useCreation(() => new Subject(), []) //By using factory function, Subject is only instantiated once.
62 | ```
63 |
64 | ## Rx.js
65 |
66 | You can use Rx.js to create a event stream. In order to prevent recreating streams, you can use the [useCreation](https://github.com/awmleer/use-creation) hook.
67 |
68 | ```jsx
69 | export function FooStore() {
70 | const event$ = useCreation(() => new Subject()) // <- Here
71 |
72 | function notify(data) {
73 | event$.next(data)
74 | }
75 |
76 | return {
77 | opened$,
78 | notify,
79 | }
80 | }
81 | ```
82 |
83 | ## useReducer
84 |
85 | If you like flux or redux, `useReducer` may be suitable for you. But we don't recommend using it.
86 |
87 |
--------------------------------------------------------------------------------
/docs/zh-cn/_coverpage.md:
--------------------------------------------------------------------------------
1 | # Reto
2 |
3 | > 基于hooks的React Store,灵活而高效。
4 |
5 | - 写Store就像写Custom Hook
6 | - 简单但高效,10分钟快速上手
7 |
8 | [GitHub](https://github.com/awmleer/reto/)
9 | [English](/)
10 | [快速上手](./get-started.md)
11 |
12 | 
13 |
14 | 
15 |
--------------------------------------------------------------------------------
/docs/zh-cn/_sidebar.md:
--------------------------------------------------------------------------------
1 | * [快速上手](./get-started.md)
2 | * [使用指南](./guide.md)
3 | * [进阶用法](./advanced.md)
4 | * [常见问题](./faq.md)
5 | * [第三方库](./third-party-libraries.md)
6 |
--------------------------------------------------------------------------------
/docs/zh-cn/advanced.md:
--------------------------------------------------------------------------------
1 | ## 传递参数给Store
2 |
3 | 可以通过Provider的args属性传递参数给Store:
4 |
5 | ```jsx
6 | export function FooStore(initial = 1) {
7 | const [x, setX] = useState(initial)
8 | return {
9 | x,
10 | setX
11 | }
12 | }
13 | ```
14 |
15 | ```jsx
16 |
17 |
18 |
19 | ```
20 |
21 | ## 在类组件中使用
22 |
23 | 虽然Reto自身是通过hooks实现的,但是也是支持在类组件中使用的。显然,我们不能在类组件中使用`useStore`,但是Reto提供了可以在类组件中使用的`Consumer`:
24 |
25 | ```jsx
26 | import {Consumer} from 'reto'
27 |
28 | export class App extends Component {
29 | render() {
30 | return (
31 |
32 | {fooStore => (
33 | fooStore.x
34 | )}
35 |
36 | )
37 | }
38 | }
39 | ```
40 |
41 | ## 如何解决Store频繁更新所导致的性能问题
42 |
43 | 解决性能问题有以下几种思路:
44 |
45 | ### 避免Provider的冗余更新
46 |
47 | 当父组件重渲染时,其中的`Provider`会跟着重新进行渲染,Store Hook也会被重新执行,最终导致订阅了Store的组件也被重新渲染,这种级联性的更新往往会造成非常大的性能损耗。
48 |
49 | ```jsx
50 | function App(props) {
51 | return (
52 |
53 | {props.children}
54 |
55 | )
56 | }
57 | ```
58 |
59 | 所幸reto为`Provider`提供了`memo`属性,可以避免掉`Provider`的冗余更新。
60 |
61 | ```jsx
62 | function App(props) {
63 | return (
64 |
65 | {props.children}
66 |
67 | )
68 | }
69 | ```
70 |
71 | 当开启`memo`时,`Provider`会对新旧`args`数组进行一次浅比较,如果没有变化,则会跳过这一次的重渲染。
72 |
73 | > 绝大多数情况下,我们都推荐开启`memo`,这对性能的提升非常直接且有效。
74 |
75 | ### 选择性订阅
76 |
77 | `useStore`支持传入一个额外的`deps`函数,来控制是否进行组件的重渲染:
78 |
79 | ```jsx
80 | const fooStore = useStore(FooStore, store => [store.x > 10, store.x < 20])
81 | ```
82 |
83 | 这和`useMemo`、`useEffect`的`deps`非常相似,但是,`useStore`的`deps`参数是一个**函数**。
84 |
85 | ### 拆分Store
86 |
87 | 我们建议对一个庞大的Store进行拆分,这样不仅代码更易于维护,性能也会有所改善。
88 |
89 | ## 获取Ref
90 |
91 | 如果你需要获取Store的Ref,可以使用`Provider`的`storeRef`属性:
92 |
93 | ```jsx
94 | const storeRef = useRef()
95 | function increase() {
96 | storeRef.current.setCount(count + 1)
97 | }
98 | return (
99 |
100 | {/*...*/}
101 |
102 | )
103 | ```
104 |
--------------------------------------------------------------------------------
/docs/zh-cn/faq.md:
--------------------------------------------------------------------------------
1 | ## Reto是hooks版本的redux么?
2 |
3 | 不是,Reto是redux和redux-saga的替代品。它在工程项目中做的事情和redux一样(构建应用的模型层),但是它所采用的方案却更为灵活高效。
4 |
5 | ## 我可以在生产环境中使用Reto么?
6 |
7 | 是的,笔者所在的一个阿里巴巴的项目组目前就在使用Reto。
8 |
9 | ## Reto会明显增大应用的打包体积么?
10 |
11 | 不会的,Reto非常的简单轻量。
12 |
13 | ## 我可以在TypeScript的项目中使用Reto么?
14 |
15 | 当然可以,Reto本身就是用TypeScript写的。
16 |
17 | ## 我可以在React 16.7及以下的环境中使用Reto么?
18 |
19 | 不可以。。Reto基于React 16.8新发布的"hooks"特性,所以如果你想使用Reto,你需要先将React升级到16.8或者更高。
20 |
21 | ## 我不写函数组件,那我还可以使用Reto么?
22 |
23 | 可以,Reto和类组件是兼容的。你可以只在store中使用hooks。
24 |
--------------------------------------------------------------------------------
/docs/zh-cn/get-started.md:
--------------------------------------------------------------------------------
1 | 基于hooks的React Store,灵活而高效。
2 |
3 | ```
4 | ___ __
5 | / _ \___ ___ _____/ /_
6 | / , _/ -_) / _ `/ __/ __/
7 | ____ /_/|_|\__/ \_,_/\__/\__/
8 | / __/ / /____ _______
9 | _\ \ / __/ _ \ / __/ -_)
10 | /___/ \__/\___/ /_/ \__/
11 |
12 | ```
13 |
14 |
15 | ## 特性
16 |
17 | - 支持全部的React Hooks,写Store就像写Custom Hook
18 | - 简单但高效,10分钟快速上手
19 | - 从此告别单一状态树,可定义多个store,随用随取
20 | - 基于React Context的依赖注入
21 | - 强类型支持,但同时兼容js环境
22 |
23 | ## 在线体验
24 |
25 |
26 |
27 | ## 常见问题
28 |
29 | [这里](https://)是一些常见的问题。
30 |
31 | ## 安装
32 |
33 | ```bash
34 | $ yarn add reto
35 | # or
36 | $ npm install reto --save
37 | ```
38 |
39 | ## 一个简单的例子
40 |
41 | 每一个`Store`其实就是一个类似于custom hook的函数。在`Store`的函数体中,你可以随意使用react hooks,例如`useState`、`useEffect`、`useRef`。
42 |
43 | ```jsx
44 | export function CounterStore() {
45 | const [count, setCount] = useState(1)
46 |
47 | useEffect(() => {
48 | console.log('x is updated.')
49 | }, [count])
50 |
51 | function increase() {
52 | setCount(count + 1)
53 | }
54 |
55 | return {
56 | count,
57 | increase,
58 | }
59 | }
60 | ```
61 |
62 | 通过`Provider`组件提供一个`FooStore`的"实例"。
63 |
64 | ```jsx
65 | import {Provider} from 'reto'
66 |
67 |
68 |
69 |
70 | ```
71 |
72 | 在组件中通过`useStore`获取并订阅`FooStore`的更新。
73 |
74 | ```jsx
75 | import {useStore} from 'reto'
76 |
77 | const App: FC = (props) => {
78 | const counterStore = useStore(CounterStore)
79 |
80 | return (
81 |