Skip to content

Commit 079bcb8

Browse files
committed
updated readme with React.context removal
1 parent 5986a67 commit 079bcb8

File tree

4 files changed

+86
-74
lines changed

4 files changed

+86
-74
lines changed
File renamed without changes.

dev.md renamed to docs/dev.md

File renamed without changes.

docs/motivation.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
## 📚 motivation
2+
3+
- React is no more a `View` lib, it's now (v17) a complete framework: so either we pick a lighter lib for the `View`, or choosing React ❌ **we shouldn't need to use an additional external framework** such as Redux, MobX, RxJs, Recoil, Zustand, Jotail...
4+
5+
- A first approach could be to use local states and sporadic use of React context, like [explained here by Kent C. Dodds](https://kentcdodds.com/blog/application-state-management-with-react), but ❌ it's not a proper Flux implementation, I'd rather have **my entire app state fully separated from the `View`**, and "connect" [containers](https://medium.com/@learnreact/container-components-c0e67432e005), mapping sub-states to the views, the way Redux allows to.
6+
7+
- Using React context to propagate the global app state, like [suggested here by Rico Sta. Cruz](https://ricostacruz.com/til/state-management-with-react-hooks), [or here by Ebenezer Don](https://blog.logrocket.com/use-hooks-and-context-not-react-and-redux/), would be ok for a _small application_, but would quickly lead to ❌ **tons of useless re-renderings**.
8+
9+
- That would eventually lead to lots of specific `useMemo` on every component requiring performance optimisation.
10+
So rather than to put the effort on developping on a proper state/component architecture, your effort will be spent on ❌ **writing those `useMemo` everywhere**.
11+
12+
- Eventually, all these steps lead me to `RxJs` which allows the use of **many stores**, by subscribing to their updates on `useEffect` and applying changes with `useState` I would have this **local rerendering** I want.
13+
❌ Well, that would mean adding a third party lib, and I'd like not to.
14+
15+
- Finally, I've come accross [zustand](https://github.com/pmndrs/zustand), which resolves all these previous points and use React hooks only !
16+
That is the lib **the closest** to the way I want to manage my state, but ❌ I want to keep my redux-like reducers, actions and container connect function to map my stores locally to the required props for the finest re-rendering, and no extra-recipes (back to point 1)
17+
18+
## 🧙 experimentation
19+
20+
The idea with `Hookstores` is
21+
22+
- ✅ to stay within React **only**,
23+
- ✅ to implement a simple [Flux architecture](https://facebook.github.io/flux/docs/in-depth-overview)
24+
-**splitting** the global app state into **stores** states,
25+
- ✅ applying **local rendering**, by mapping these stores states to [containers](https://medium.com/@learnreact/container-components-c0e67432e005), using React hooks `useState` and `useEffect`.
26+
- ✅ using React hooks to create the stores on app startup, then access `{dispatch, ...stores} = useStores()` everywhere:
27+
28+
- `dispatch` allowing to emit actions to every store, and they now if they have to compute this action to reduce a new state.
29+
30+
- your `stores` are accessible, with the `key` you give in `descriptions`, to allow kind of "mapStateToProps" on the connected containers, only when there _is_ an update: Now that's local re-rendering.
31+
32+
## ☢️ disclaimer
33+
34+
So yes, somehow it ends up as another lib to manage your React state 🙃.
35+
36+
But since it's only few files you should understand what's behind the hood, then use and tweak them to your convenience _within your own React app_ rather than use it out of the box.
37+
38+
You'll see `Hookstores` is rather few lines to **patch** React.
39+
40+
Furthermore,
41+
42+
- ⚠️ it's not written in typescript 🙀
43+
- ⚠️ there are no tests 💥
44+
45+
That being said,
46+
47+
- ✅ I'm confidently using this implementation between many apps,
48+
- ✅ so I prefer to have this package,
49+
- ✅ so why not sharing this experiment.

readme.md

Lines changed: 37 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,15 @@
44
[![devDeps](https://david-dm.org/uralys/hookstores/dev-status.svg)](https://david-dm.org/uralys/hookstores?type=dev)
55
[![deps](https://david-dm.org/uralys/hookstores/status.svg)](https://david-dm.org/uralys/hookstores)
66

7-
`Hookstores` is an elementary [Flux](https://facebook.github.io/flux/docs/in-depth-overview) implementation using React hooks and context.
7+
`Hookstores` is an elementary [Flux](https://facebook.github.io/flux/docs/in-depth-overview) implementation using React hooks.
88

99
![action->dispatcher->store->view](https://facebook.github.io/flux/img/overview/flux-simple-f8-diagram-1300w.png)
1010

1111
---
1212

1313
## 📚 motivation
1414

15-
- React is no more a `View` lib, it's now (v17) a complete framework: so either we pick a lighter lib for the `View`, or choosing React ❌ **we shouldn't need to use an additional external framework** such as Redux, MobX, RxJs, Recoil, Jotail...
16-
17-
- A first approach could be to use local states and sporadic use of React context, like [explained here by Kent C. Dodds](https://kentcdodds.com/blog/application-state-management-with-react), but ❌ it's not a proper Flux implementation, I'd rather have **my entire app state fully separated from the `View`**, and "connect" [containers](https://medium.com/@learnreact/container-components-c0e67432e005), mapping sub-states to the views, the way Redux allows to.
18-
19-
- Using React context to propagate the global app state, like [suggested here by Rico Sta. Cruz](https://ricostacruz.com/til/state-management-with-react-hooks), [or here by Ebenezer Don](https://blog.logrocket.com/use-hooks-and-context-not-react-and-redux/), would be ok for a _small application_, but would quickly lead to ❌ **tons of useless re-renderings**.
20-
21-
- That would eventually lead to lots of specific `useMemo` on every component requiring performance optimisation.
22-
So rather than to put the effort on developping on a proper state/component architecture, your effort will be spent on ❌ **writing those `useMemo` everywhere**.
23-
24-
- Eventually, all these steps lead me to `RxJs` which allows the use of **many stores**, by subscribing to their updates on `useEffect` and applying changes with `useState` I would have this **local rerendering** I want.
25-
❌ Well, that would mean adding a third party lib, and I'd like not to.
26-
27-
## 🧙 experimentation
28-
29-
The idea with `Hookstores` is
30-
31-
- ✅ to stay within React **only**,
32-
- ✅ to implement a simple [Flux architecture](https://facebook.github.io/flux/docs/in-depth-overview)
33-
-**splitting** the global app state into **stores** states,
34-
- ✅ applying **local rendering**, by mapping these stores states to [containers](https://medium.com/@learnreact/container-components-c0e67432e005), using React hooks `useState` and `useEffect`.
35-
- ✅ using React context only to provide `Hookstores` with `{dispatch, ...stores}` everywhere,
36-
37-
- `dispatch` allowing to emit actions to every store, and they now if they have to compute this action to reduce a new state.
38-
39-
- your `stores` are accessible, with the `key` you give in `descriptions`, to allow kind of "mapStateToProps" on the connected containers, only when there _is_ an update: Now that's local re-rendering.
40-
41-
## ☢️ disclaimer
42-
43-
So yes, somehow it ends up as another lib to manage your React state 🙃.
44-
45-
But since it's only few files you should understand what's behind the hood, then use and tweak them to your convenience _within your own React app_ rather than use it out of the box.
46-
47-
You'll see `Hookstores` is rather few lines to **patch** React.
48-
49-
Furthermore,
50-
51-
- ⚠️ it's not written in typescript 🙀
52-
- ⚠️ there are no tests 💥
53-
54-
That being said,
55-
56-
- ✅ I'm confidently using this implementation between many apps,
57-
- ✅ so I prefer to have this package,
58-
- ✅ so why not sharing this experiment.
15+
read this [doc]('docs/motivation.md')
5916

6017
---
6118

@@ -69,15 +26,12 @@ That being said,
6926

7027
## 🛠 setup
7128

72-
Under the hood, the `Hookstores` uses [React Context](https://reactjs.org/docs/context.html).
29+
Here is the path to follow to setup Hookstores on your app:
7330

74-
As such, you can wrap your root component, such as `App`, with the provider called `Hookstores` for convenience, to integrate it with your React app.
75-
76-
```jsx
77-
<Hookstores>
78-
<App />
79-
</Hookstores>
80-
```
31+
- 1: write descriptions: your redux-like reducers
32+
- 2: create stores on startup
33+
- 3: bind store data to your container state
34+
- 4: dispatch actions to trigger stores changes
8135

8236
---
8337

@@ -89,10 +43,12 @@ In the following, let's illustrate how to use `Hookstores` with:
8943

9044
---
9145

92-
## ✍️ descriptions
46+
## 1 ✍️ descriptions
9347

9448
`Hookstores` will create stores, register for actions, and emit updates using the descriptions you provide.
9549

50+
![computeAction](https://user-images.githubusercontent.com/910636/103582817-e2d13600-4ede-11eb-8fbf-f0eb2a7cd3e7.png)
51+
9652
Each store has its own description, which must export:
9753

9854
- an `initialState`,
@@ -114,9 +70,10 @@ export default itemsStoreDescription;
11470
- you should use one store for one feature (here the `Items`)
11571
- define within `computeAction` how a store must update its state for every `handledAction`.
11672

117-
![computeAction](https://user-images.githubusercontent.com/910636/103582817-e2d13600-4ede-11eb-8fbf-f0eb2a7cd3e7.png)
73+
<details>
74+
<summary>Example</summary>
11875

119-
Here is the example for our illustrating `itemsStore`:
76+
Here is the example for our illustrating `itemsStore`
12077

12178
```js
12279
/* ./features/items/store-description.js */
@@ -150,46 +107,48 @@ export default itemsStoreDescription;
150107
export {FETCH_ITEMS};
151108
```
152109

110+
</details>
111+
153112
---
154113

155-
## 🛠 back to setup
114+
## 2 🏁 creating the stores on startup
156115

157-
Once all descriptions are ready, you give them names, and pass them as parameters to `<Hookstores>`
116+
Once all descriptions are ready, you give them names, and pass them as parameters to `createStores()`
158117

159118
```js
160119
/* ./index.js */
161120

162121
import itemsStore from './features/items/store-description.js';
163122
import anyOtherStore from './features/whatever/store-description.js';
164123

165-
<Hookstores
166-
descriptions={{
167-
quizzesStore,
168-
anyOtherStore
169-
}}
170-
>
171-
<App />
172-
</Hookstores>;
124+
createStores({
125+
itemsStore,
126+
anyOtherStore
127+
});
173128
```
174129

175130
---
176131

177-
## storeState ➡️ props
132+
## 3: storeState ➡️ props
178133

179-
Use `connectStore` to register your container to store changes.
134+
- Retrieve the store to connect provided by `useStores`.
135+
- Use `connectStore` to register your container to store changes.
136+
- Then map the store state to your component props, using your container state.
137+
<details>
138+
<summary>Example</summary>
180139

181-
Then map the store state to your component props, using your container state.
140+
Here is the example for our illustrating `itemsStore`
182141

183142
```js
184143
/* ./features/items/container.js */
185144

186145
import React, {useLayoutEffect, useState} from 'react';
187146
import ItemsComponent from './component';
188-
import {connectStore} from 'hookstores';
147+
import {connectStore, useStores} from 'hookstores';
189148

190149
const ItemsContainer = props => {
191150
const [items, setItems] = useState();
192-
const {itemsStore} = props;
151+
const {itemsStore} = useStores();
193152

194153
useLayoutEffect(() => {
195154
const onStoreUpdate = storeState => {
@@ -205,20 +164,24 @@ const ItemsContainer = props => {
205164
};
206165
```
207166

167+
## </details>
168+
169+
---
170+
208171
🔍 Don't forget to return the `disconnect` function at the end of your hook, unless you will have stores updates triggering unmounted containers updates.
209172

210173
---
211174

212-
## 📡 dispatching actions
175+
## 4 📡 dispatching actions
213176

214177
Use [`prop drilling`](https://kentcdodds.com/blog/prop-drilling) from your containers to your components: pass functions dispatching the actions
215178

216179
```js
217-
import {useHookstores} from 'hookstores';
180+
import {useStores} from 'hookstores';
218181
import {SELECT_ITEM} from 'path/to/actions';
219182

220183
const ItemsContainer = props => {
221-
const {dispatch} = useHookstores();
184+
const {dispatch} = useStores();
222185

223186
const selectItem = id => () => {
224187
dispatch({

0 commit comments

Comments
 (0)