You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+49-36Lines changed: 49 additions & 36 deletions
Original file line number
Diff line number
Diff line change
@@ -44,19 +44,31 @@ In my opinion, a robust state management system should effectively adhere to the
44
44
45
45
This library is created to address the concerns mentioned above.
46
46
47
-
###Controller
47
+
## Controller
48
48
49
-
Defining a `Controller` for a view or a view group in subclass manner will benefit from these criterias:
49
+
### Overview
50
50
51
-
-`Inter-Controller Subscription`:`Controller`can subscribe to and depend on each other, allowing for the internal triggering of state emissions.
51
+
When defining a`Controller`for a React view or view group, adopting a subclass approach brings several advantages:
52
52
53
-
-`Inheritance and Extensibility`: By making use of inheritance, a `Controller` can be further extended, leading to more reusable code.
53
+
#### 1. Inter-Controller Subscription
54
54
55
-
-`Property Manipulation`: Creating, accessing, and modifying properties within a Controller becomes straightforward.
55
+
`Controller` instances can seamlessly subscribe to and depend on each other, enabling internal triggers for state emissions.
56
56
57
-
The `State` object is something that holds data for UI rendering, and the UI uses `State` for its rendering process.
57
+
#### 2. Inheritance and Extensibility
58
58
59
-
Take a look at some available function in the base `Controller` class. We won't have to re-write any of this, unless we want to.
59
+
Utilizing inheritance facilitates the extension of a `Controller`, promoting the creation of more reusable code.
60
+
61
+
#### 3. Property Manipulation
62
+
63
+
Creating, accessing, and modifying properties within a `Controller` becomes a straightforward process.
64
+
65
+
### State Management
66
+
67
+
The core of the state management revolves around the `State` object, responsible for holding data crucial for UI rendering. The UI utilizes the `State` object during its rendering process.
68
+
69
+
### Base Controller Class
70
+
71
+
Explore some of the available functions in the base `Controller` class. The provided functionality eliminates the need for redundant code, unless customization is desired.
60
72
61
73
```ts
62
74
abstractclassController<T> {
@@ -82,7 +94,7 @@ abstract class Controller<T> {
82
94
83
95
A `Controller` will directly interact with the `State` object to mutate it, indirectly causing UI re-rendering.
84
96
85
-
To create a new `Controller`, you need to extend the `Controller` class, which provides methods for state mutation and notifying listeners. In the example above, the `CounterController` has `{count: 0}` as its `initialState`. It updates it through methods like `increaseCounter` or `decreaseCounter` using `emit(newState)`.
97
+
To create a new `Controller`, you need to extend the `Controller` class, which provides methods for state mutation and notifying listeners. In the example below, the `CounterController` has `{count: 0}` as its `initialState`. It updates it through methods like `increaseCounter` or `decreaseCounter` using `emit(newState)`.
@@ -107,7 +119,7 @@ class CounterController extends Controller<CounterState> {
107
119
108
120
Do note that the `newState` object must be different from the old `State`. Otherwise, the `Controller` will just skip it. This optimization avoids unnecessary state updates.
109
121
110
-
When using `emit`, the `{...this.state}` is not needed. Object passed in the `emit(newState)` function will be merge with the existed current state.
122
+
When using `emit`, the `{...this.state}` is not needed to copy other contents of old state. Object passed in the `emit(newState)` function will be merge with the existed current state.
111
123
112
124
One of many common patterns is to handle all of the necessary logic to fetch data from an API in the `Controller`, then emit the data from within the `Controller`. For example:
113
125
@@ -140,7 +152,7 @@ In the UI, we can then check for the `loading` property of the `State` object an
140
152
141
153
### Provider and Dependency Injection (DI)
142
154
143
-
A `Controller` manages states for a scope of child components, and so it must be provided to them for further interaction and triggering re-render. This is similar to the `Context` API. In fact, this library uses the `Context` API internally for DI.
155
+
A `Controller` manages states for a group of child components, and so it must be provided to them for further interaction. This is similar to the `Context` API. In fact, this library uses the `Context` API internally for DI.
144
156
145
157
To provide a group of child components with a `Controller`, we can use `ControllerProvider`:
146
158
@@ -151,18 +163,19 @@ To provide a group of child components with a `Controller`, we can use `Controll
151
163
</ControllerProvider>
152
164
```
153
165
154
-
The `source` parameter take in a function to create `Controller` and keep it with `useRef` throughout the component's life-span. When the component unmounted, a clean up function will be called and automatically trigger the `dispose` function defined inside the `Controller` class, allow resources clean up.
166
+
The `source` parameter take in a function to create a `Controller` and keep it with `useRef` throughout the component's life-span. When the component unmounted, a clean up function will be called automatically, trigger the `dispose` function defined inside the `Controller` class, allowing resources clean up when a `Controller` is not needed anymore.
155
167
156
-
Another way to provide a `Controller` to children components would be using `ControllerProvider` but pass in the `source` param an instance of `Controller` instead of a create function
168
+
Another way to provide a `Controller` to children components would be using `ControllerProvider` but pass to the `source` param an instance of `Controller` instead of a create function:
157
169
158
170
```tsx
171
+
// existed isntance else where, you would have to manage the references and cleanup yourself though
But be aware that `Controller` provided this way won't be kept with `useRef` like the first one, and maybe subjected to change, clean up function also won't be called for this type of `ControllerProvider`
178
+
But be aware that `Controller` provided this way won't be kept with `useRef` like the first approach, clean up function also won't be called when this `ControllerProvider` unmounted.
166
179
167
180
The `CounterComponent` and `ButtonComponent` will now have access to the `CounterController`.
168
181
@@ -172,7 +185,7 @@ Inside the `ButtonComponent` or `CounterComponent`, you can get the provided `Co
@@ -187,12 +200,14 @@ You can interact with the provided `CounterController` as needed. Please note th
187
200
188
201
This hook should not cause re-render when new `State` from `CounterController` is emitted.
189
202
190
-
The instance passed in`ControllerProvider` will have it's name extracted and used as a resource-key for later query, this mean if you provide a class with the name `A`, you have to use the exact type to query it with `useProvider` hook.
203
+
The `Controller`instance passed to`ControllerProvider` will have it's name extracted and used as a resource-key for later query, this mean if you provide a class with the name `A`, you have to use the exact type to query it with `useProvider` hook.
191
204
192
-
But for cases that one type of `Controller` may has many implemmentations, subclass-ing each other, and we only want the dependent/client of this controller know it's base type, we can use the `ctor` param in `ProviderController`, this effectively change the query type of provided `Controller`
205
+
But for cases that one type of `Controller` may has many implemmentations, subclass-ing each other, and we only want the dependent/client of this controller know it's base type, we can use the `ctor` param in `ProviderController` to overwrite the query type.
193
206
194
207
```tsx
195
-
// query type will now become `TestController`: useProvider(TestController)
@@ -234,20 +249,18 @@ import { MultiProvider } from 'react-state-view-controller';
234
249
235
250
```
236
251
237
-
Both representations are equivalent. The nested component encompasses the others, granting access to the above `Controller` if needed.
252
+
Both representations are equivalent. The nested components wrapped each other, granting access to the above provided resources if needed.
238
253
239
-
In situations where a single `ControllerProvider` requires access to the above`Controller` to depend on it, consider separating it into a distinct component:
254
+
In situations where a single `ControllerProvider` requires access to already provided`Controller` to depend on it, consider separating it into a distinct component:
@@ -268,7 +281,7 @@ Then this component can then be used as follows:
268
281
269
282
### useAutoDispose
270
283
271
-
While `ControllerProvider` provides an effective way of DI, there are times that this is unecessary, as we just simply want to separate logic of a small view to a controller, but still want to benefit from the auto-cleanup feature.
284
+
While `ControllerProvider` provides an effective way of DI, there are times that this is unecessary, as we just simply want to separate logic of a small view to a controller, dependent child components won't go that deeply nested, but still want to benefit from the auto-cleanup feature.
272
285
273
286
In this case, we can use the `useAutoDispose` hook:
This `controller` instance will be kept with `useRef`, making it persist between re-render and will auto call the `dispose` function when current component is unmounted.
280
293
281
-
Controller created this way can also be feed into the `source` param of `ProviderController` mentioned above, preferably the second type, which `source` take in an instance, as the responsibility of maintaining instance and auto cleaning up feature is handled already by `useAutoDispose`.
294
+
Controller created this way can also be feed into the `source` param of `ProviderController` mentioned above, specifically the second approach, which the `source`param take in an instance. Because the responsibility of persisting the controller instance and auto cleaning up feature is handled already by `useAutoDispose`.
282
295
283
296
### Builder
284
297
285
298
To trigger the re-render process when new State is emitted from `Controller` within the same scope, you can use the `Builder` component.
286
299
287
300
```tsx
288
301
<Builder
289
-
// specify the type to query
302
+
// specify the class type to query
290
303
source={CounterController}
291
304
buildWhen={(prev, curr) => {
292
305
// Optional: If this function is provided and returns `false`, the re-render trigger will be skipped.
@@ -307,7 +320,7 @@ This component can also take in directly an instance of `Controller` as the `sou
307
320
```tsx
308
321
<Builder
309
322
// passing an instance
310
-
source={counterControllerInstance}
323
+
source={counterInstance}
311
324
buildWhen={(prev, curr) =>true}
312
325
>
313
326
{(state:number) => {
@@ -327,7 +340,7 @@ import { useBuilder } from 'react-state-view-controller'
(state) =>console.log(state), // callback when state changed
414
427
(prev, curr) =>true, // callback filter
415
428
)
@@ -419,9 +432,9 @@ Note that this hook is not intended by default to cause re-render, it just simpl
419
432
420
433
## For other types
421
434
422
-
Of course, a view `Controller` is not the only thing that we need to DI in a production app, we have services, repositories, models,... or other types of data.
435
+
Of course, a view `Controller` is not the only thing that we need to DI in typical production app, we also have services, repositories, models,... or other types of data.
423
436
424
-
To acommondate these, you can check out the [react-scoped-provider](https://www.npmjs.com/package/react-scoped-provider) library, it is fully compatible with this library, in fact, the `useProvider` hook is just re-import and re-export from it.
437
+
To acommondate these, you can check out the [react-scoped-provider](https://www.npmjs.com/package/react-scoped-provider) library, which is fully compatible with this library, in fact, the `useProvider` hook is just re-import and re-export from it.
0 commit comments