Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions doc-src/00-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ However, there are several caveats of React Redux / RTK:

Recently [zustand](https://zustand.docs.pmnd.rs/learn/getting-started/introduction) significantly simplify the use of Redux / RTK. However:

1. Still requires developers have their own methods for "objects-of-the-same-kind".
2. We still need to know the relationship between store vs. slice (`useBoundStore`).
3. I feel that the [`createBearFishSlice`](https://zustand.docs.pmnd.rs/learn/guides/slices-pattern#updating-multiple-stores) example is actually awkward. Why do we need to create additional slices if we want to update the states from multiple slices?
1. It still requires developers have their own methods for "objects-of-the-same-kind".
2. Developers still need to know the relationship between store vs. slice (`useBoundStore`).
3. With the [recommended single-store pattern](https://zustand.docs.pmnd.rs/learn/guides/flux-inspired-practice), the selectors can be confusing. For example, is `increment` is a selector for `useBearStore` or `useFishStore`? zustand appears to address this issue with [createSelectors](https://zustand.docs.pmnd.rs/learn/guides/auto-generating-selectors), which adds `.use.[selector]()` functions for accessing state properties and actions.
4. I feel that the [Bear and Fish example](https://zustand.docs.pmnd.rs/learn/guides/slices-pattern) is somewhat awkward because `Bear.eatFish()` assumes the existence of `state.fishes` and contaminates the `Fish` state.
5. I also feel that the [`createBearFishSlice`](https://zustand.docs.pmnd.rs/learn/guides/slices-pattern#updating-multiple-stores) example awkward. Why is it necessary to create an additional slice simply to update state across multiple slices?

## Modularized Thunk is All We Need

Expand Down
26 changes: 20 additions & 6 deletions doc-src/03-how-it-works.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
# How It Works

## [ThunkModuleMap](https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkContext/thunkModuleMap.ts): the Single Source of Truth
## [ThunkModuleMap](https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/thunkModuleMap.ts): the Single Source of Truth

All the states are managed in a single source of truth: [ThunkModuleMap](https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/thunkModuleMap.ts).

## Object-`State`

Object-states are typically used for component-presentation. Therefore, Object-states require renew as new objects after each operation for ReactJS to detect the change of the state.

## `ModuleState` and `NodeState`

We realized that developers mostly care only the object-states. `ModuleState` and `NodeState` are never renewed as new objects after each operations. This approach enables us to have `getStateByModule` to obtain the newest object-state while keeping object-states copy-on-write.
We realized that developers care only the object-states. `ModuleState`s are registered through [`registerThunk`](https://github.com/chhsiao1981/use-thunk/blob/main/src/registerThunk.ts) and never renewed as new objects after each operation. `NodeState`s are never renewed during [`update`](https://github.com/chhsiao1981/use-thunk/blob/main/src/defaultThunkFuncs/update.ts#L61) or [`upsert`](https://github.com/chhsiao1981/use-thunk/blob/main/src/defaultThunkFuncs/upsert.ts#L66). This approach enables us to have `getStateByModule` to obtain the newest object-state while keeping object-states copy-on-write.

## Following Action-Dispatch-Reducer Pattern Under The Hood.

Despite that we need only the thunk modules when using `use-thunk`, the implementation heavily utilizes action-dispatch-reducer pattern under the hood:

* The implementation of `dispatch` can be found [here](https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L50).
* [`getModuleState`](https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L36) can be viewed as [the original `getState` in Redux Thunk](https://redux.js.org/usage/writing-logic-thunks).
* [`set`](https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L98) is `dispatch` and the syntax sugar of `dispatch(upsert(id, data))`.
* [`get`](https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L45) is the syntax sugar of getting the object-state from module state.
* [`getOrNull`](https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L40) is the variation of `get`.

## Reducers: [Only Primitive Reducers](https://github.com/chhsiao1981/use-thunk/blob/main/src/reducer/defaultReduceMap.ts)

We recognize that state management requires only `init`, `get`, `update`, and `remove` ([CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)). Furthermore, in most cases, only `upsert` and `get` are needed. Therefore, our implementation provides [only these primitive reducers](https://github.com/chhsiao1981/use-thunk/blob/main/src/reducer/defaultReduceMap.ts).

## Separation of `doModule` and `ModuleState`

Within a module, we realized that data and operations can be separated for easy maintenance, as there should be same module functions operating on different objects. Therefore, we have `doMod` to get the module functions, and `getMod` to get the module states.
Unlike the selector pattern used by RTK and Zustand, we believe that data and operations should be separated to improve maintainability, since the same module functions should be able to operate on different objects. Therefore, we provide [`doMod`](https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/doModule.ts#L69) for accessing module functions and [`getMod`](https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/thunkModuleMap.ts#L22) for accessing module state.

## Object-based Re-rendering

Starting 16.0.0, we use `useSyncExternalStore` for each object to achieve object-based re-rendering.

## Reducer: Only Primitive Reducers
Starting 16.1.0, we use [`useSyncExternalStore`](https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L23) for [each object](https://github.com/chhsiao1981/use-thunk/blob/main/src/states/node.ts#L41) to achieve object-based re-rendering.
8 changes: 5 additions & 3 deletions docs/00-introduction/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -651,9 +651,11 @@ <h2 id="caveats-of-react-redux-and-redux-toolkit-rtk">Caveats of React Redux (an
<h2 id="caveats-of-zustand">Caveats of zustand</h2>
<p>Recently <a href="https://zustand.docs.pmnd.rs/learn/getting-started/introduction">zustand</a> significantly simplify the use of Redux / RTK. However:</p>
<ol>
<li>Still requires developers have their own methods for "objects-of-the-same-kind".</li>
<li>We still need to know the relationship between store vs. slice (<code>useBoundStore</code>).</li>
<li>I feel that the <a href="https://zustand.docs.pmnd.rs/learn/guides/slices-pattern#updating-multiple-stores"><code>createBearFishSlice</code></a> example is actually awkward. Why do we need to create additional slices if we want to update the states from multiple slices?</li>
<li>It still requires developers have their own methods for "objects-of-the-same-kind".</li>
<li>Developers still need to know the relationship between store vs. slice (<code>useBoundStore</code>).</li>
<li>With the <a href="https://zustand.docs.pmnd.rs/learn/guides/flux-inspired-practice">recommended single-store pattern</a>, the selectors can be confusing. For example, is <code>increment</code> is a selector for <code>useBearStore</code> or <code>useFishStore</code>? zustand appears to address this issue with <a href="https://zustand.docs.pmnd.rs/learn/guides/auto-generating-selectors">createSelectors</a>, which adds <code>.use.[selector]()</code> functions for accessing state properties and actions.</li>
<li>I feel that the <a href="https://zustand.docs.pmnd.rs/learn/guides/slices-pattern">Bear and Fish example</a> is somewhat awkward because <code>Bear.eatFish()</code> assumes the existence of <code>state.fishes</code> and contaminates the <code>Fish</code> state.</li>
<li>I also feel that the <a href="https://zustand.docs.pmnd.rs/learn/guides/slices-pattern#updating-multiple-stores"><code>createBearFishSlice</code></a> example awkward. Why is it necessary to create an additional slice simply to update state across multiple slices?</li>
</ol>
<h2 id="modularized-thunk-is-all-we-need">Modularized Thunk is All We Need</h2>
<p>React Redux, zustand, and many other GSM frameworks focus on: "We have stores (ideally a single store as single-source-of-truth) that manage the states. How do we manage the stores."</p>
Expand Down
67 changes: 50 additions & 17 deletions docs/03-how-it-works/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -421,32 +421,43 @@
</li>

<li class="md-nav__item">
<a href="#separation-of-domodule-and-modulestate" class="md-nav__link">
<a href="#following-action-dispatch-reducer-pattern-under-the-hood" class="md-nav__link">
<span class="md-ellipsis">

Separation of doModule and ModuleState
Following Action-Dispatch-Reducer Pattern Under The Hood.

</span>
</a>

</li>

<li class="md-nav__item">
<a href="#object-based-re-rendering" class="md-nav__link">
<a href="#reducers-only-primitive-reducers" class="md-nav__link">
<span class="md-ellipsis">

Object-based Re-rendering
Reducers: Only Primitive Reducers

</span>
</a>

</li>

<li class="md-nav__item">
<a href="#reducer-only-primitive-reducers" class="md-nav__link">
<a href="#separation-of-domodule-and-modulestate" class="md-nav__link">
<span class="md-ellipsis">

Reducer: Only Primitive Reducers
Separation of doModule and ModuleState

</span>
</a>

</li>

<li class="md-nav__item">
<a href="#object-based-re-rendering" class="md-nav__link">
<span class="md-ellipsis">

Object-based Re-rendering

</span>
</a>
Expand Down Expand Up @@ -548,32 +559,43 @@
</li>

<li class="md-nav__item">
<a href="#separation-of-domodule-and-modulestate" class="md-nav__link">
<a href="#following-action-dispatch-reducer-pattern-under-the-hood" class="md-nav__link">
<span class="md-ellipsis">

Separation of doModule and ModuleState
Following Action-Dispatch-Reducer Pattern Under The Hood.

</span>
</a>

</li>

<li class="md-nav__item">
<a href="#object-based-re-rendering" class="md-nav__link">
<a href="#reducers-only-primitive-reducers" class="md-nav__link">
<span class="md-ellipsis">

Object-based Re-rendering
Reducers: Only Primitive Reducers

</span>
</a>

</li>

<li class="md-nav__item">
<a href="#reducer-only-primitive-reducers" class="md-nav__link">
<a href="#separation-of-domodule-and-modulestate" class="md-nav__link">
<span class="md-ellipsis">

Reducer: Only Primitive Reducers
Separation of doModule and ModuleState

</span>
</a>

</li>

<li class="md-nav__item">
<a href="#object-based-re-rendering" class="md-nav__link">
<span class="md-ellipsis">

Object-based Re-rendering

</span>
</a>
Expand Down Expand Up @@ -601,16 +623,27 @@


<h1 id="how-it-works">How It Works</h1>
<h2 id="thunkmodulemap-the-single-source-of-truth"><a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkContext/thunkModuleMap.ts">ThunkModuleMap</a>: the Single Source of Truth</h2>
<h2 id="thunkmodulemap-the-single-source-of-truth"><a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/thunkModuleMap.ts">ThunkModuleMap</a>: the Single Source of Truth</h2>
<p>All the states are managed in a single source of truth: <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/thunkModuleMap.ts">ThunkModuleMap</a>.</p>
<h2 id="object-state">Object-<code>State</code></h2>
<p>Object-states are typically used for component-presentation. Therefore, Object-states require renew as new objects after each operation for ReactJS to detect the change of the state.</p>
<h2 id="modulestate-and-nodestate"><code>ModuleState</code> and <code>NodeState</code></h2>
<p>We realized that developers mostly care only the object-states. <code>ModuleState</code> and <code>NodeState</code> are never renewed as new objects after each operations. This approach enables us to have <code>getStateByModule</code> to obtain the newest object-state while keeping object-states copy-on-write.</p>
<p>We realized that developers care only the object-states. <code>ModuleState</code>s are registered through <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/registerThunk.ts"><code>registerThunk</code></a> and never renewed as new objects after each operation. <code>NodeState</code>s are never renewed during <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/defaultThunkFuncs/update.ts#L61"><code>update</code></a> or <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/defaultThunkFuncs/upsert.ts#L66"><code>upsert</code></a>. This approach enables us to have <code>getStateByModule</code> to obtain the newest object-state while keeping object-states copy-on-write.</p>
<h2 id="following-action-dispatch-reducer-pattern-under-the-hood">Following Action-Dispatch-Reducer Pattern Under The Hood.</h2>
<p>Despite that we need only the thunk modules when using <code>use-thunk</code>, the implementation heavily utilizes action-dispatch-reducer pattern under the hood:</p>
<ul>
<li>The implementation of <code>dispatch</code> can be found <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L50">here</a>.</li>
<li><a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L36"><code>getModuleState</code></a> can be viewed as <a href="https://redux.js.org/usage/writing-logic-thunks">the original <code>getState</code> in Redux Thunk</a>.</li>
<li><a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L98"><code>set</code></a> is <code>dispatch</code> and the syntax sugar of <code>dispatch(upsert(id, data))</code>.</li>
<li><a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L45"><code>get</code></a> is the syntax sugar of getting the object-state from module state.</li>
<li><a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L40"><code>getOrNull</code></a> is the variation of <code>get</code>.</li>
</ul>
<h2 id="reducers-only-primitive-reducers">Reducers: <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/reducer/defaultReduceMap.ts">Only Primitive Reducers</a></h2>
<p>We recognize that state management requires only <code>init</code>, <code>get</code>, <code>update</code>, and <code>remove</code> (<a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a>). Furthermore, in most cases, only <code>upsert</code> and <code>get</code> are needed. Therefore, our implementation provides <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/reducer/defaultReduceMap.ts">only these primitive reducers</a>.</p>
<h2 id="separation-of-domodule-and-modulestate">Separation of <code>doModule</code> and <code>ModuleState</code></h2>
<p>Within a module, we realized that data and operations can be separated for easy maintenance, as there should be same module functions operating on different objects. Therefore, we have <code>doMod</code> to get the module functions, and <code>getMod</code> to get the module states.</p>
<p>Unlike the selector pattern used by RTK and Zustand, we believe that data and operations should be separated to improve maintainability, since the same module functions should be able to operate on different objects. Therefore, we provide <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/doModule.ts#L69"><code>doMod</code></a> for accessing module functions and <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/thunkModule/thunkModuleMap.ts#L22"><code>getMod</code></a> for accessing module state.</p>
<h2 id="object-based-re-rendering">Object-based Re-rendering</h2>
<p>Starting 16.0.0, we use <code>useSyncExternalStore</code> for each object to achieve object-based re-rendering.</p>
<h2 id="reducer-only-primitive-reducers">Reducer: Only Primitive Reducers</h2>
<p>Starting 16.1.0, we use <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/useThunk/useThunkReducer.ts#L23"><code>useSyncExternalStore</code></a> for <a href="https://github.com/chhsiao1981/use-thunk/blob/main/src/states/node.ts#L41">each object</a> to achieve object-based re-rendering.</p>



Expand Down
2 changes: 1 addition & 1 deletion docs/search/search_index.json

Large diffs are not rendered by default.

Loading