Skip to content

Commit 2dae01c

Browse files
committed
edit readme
1 parent 6d0154b commit 2dae01c

File tree

1 file changed

+49
-36
lines changed

1 file changed

+49
-36
lines changed

README.md

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,31 @@ In my opinion, a robust state management system should effectively adhere to the
4444

4545
This library is created to address the concerns mentioned above.
4646

47-
### Controller
47+
## Controller
4848

49-
Defining a `Controller` for a view or a view group in subclass manner will benefit from these criterias:
49+
### Overview
5050

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:
5252

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
5454

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.
5656

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
5858

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.
6072

6173
```ts
6274
abstract class Controller<T> {
@@ -82,7 +94,7 @@ abstract class Controller<T> {
8294

8395
A `Controller` will directly interact with the `State` object to mutate it, indirectly causing UI re-rendering.
8496

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)`.
8698

8799
```ts
88100
import { Controller } from 'react-state-view-controller'
@@ -107,7 +119,7 @@ class CounterController extends Controller<CounterState> {
107119

108120
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.
109121

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.
111123

112124
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:
113125

@@ -140,7 +152,7 @@ In the UI, we can then check for the `loading` property of the `State` object an
140152

141153
### Provider and Dependency Injection (DI)
142154

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.
144156

145157
To provide a group of child components with a `Controller`, we can use `ControllerProvider`:
146158

@@ -151,18 +163,19 @@ To provide a group of child components with a `Controller`, we can use `Controll
151163
</ControllerProvider>
152164
```
153165

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.
155167

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:
157169

158170
```tsx
171+
// existed isntance else where, you would have to manage the references and cleanup yourself though
159172
<ControllerProvider source={counterControllerInstance}>
160173
<CounterComponent />
161174
<ButtonComponent />
162175
</ControllerProvider>
163176
```
164177

165-
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.
166179

167180
The `CounterComponent` and `ButtonComponent` will now have access to the `CounterController`.
168181

@@ -172,7 +185,7 @@ Inside the `ButtonComponent` or `CounterComponent`, you can get the provided `Co
172185
import { useProvider } from 'react-state-view-controller'
173186

174187
const ButtonComponent = () => {
175-
// pass in the type of provided controller
188+
// pass in the type of class
176189
const controller = useProvider(CounterController)
177190
return (
178191
<div>
@@ -187,12 +200,14 @@ You can interact with the provided `CounterController` as needed. Please note th
187200

188201
This hook should not cause re-render when new `State` from `CounterController` is emitted.
189202

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.
191204

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.
193206

194207
```tsx
195-
// query type will now become `TestController`: useProvider(TestController)
208+
// query type will now become `TestController`
209+
// else where:
210+
// useProvider(TestController)
196211
<ControllerProvider ctor={TestController} source={()=> SubclassOfTestController()}>
197212
<Screen />
198213
</ControllerProvider>,
@@ -234,20 +249,18 @@ import { MultiProvider } from 'react-state-view-controller';
234249

235250
```
236251

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.
238253

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:
240255

241256
```tsx
242257
import { useProvider } from 'react-state-view-controller'
243258

244-
const Counter2Provider = ({ children }) => {
259+
const DependentProvider = ({ children }) => {
245260
// We can get the CounterController here because it's provided is above this component.
246261
const controller = useProvider(CounterController)
247-
// do something with CounterController
248-
// ...
249262

250-
return <ControllerProvider create={() => new CounterController2()}>{children}</ControllerProvider>
263+
return <ControllerProvider create={() => new DependentController(controller)}>{children}</ControllerProvider>
251264
}
252265
```
253266

@@ -257,7 +270,7 @@ Then this component can then be used as follows:
257270
<MultiProvider
258271
providers={[
259272
<ControllerProvider create={() => new CounterController()} />,
260-
<Counter2Provider />,
273+
<DependentProvider />,
261274
<ControllerProvider create={() => new CounterController3()} />,
262275
<ControllerProvider create={() => new CounterController4()} />,
263276
]}
@@ -268,7 +281,7 @@ Then this component can then be used as follows:
268281

269282
### useAutoDispose
270283

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.
272285

273286
In this case, we can use the `useAutoDispose` hook:
274287

@@ -278,15 +291,15 @@ const controller = useAutoDispose(() => new CounterController())
278291

279292
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.
280293

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`.
282295

283296
### Builder
284297

285298
To trigger the re-render process when new State is emitted from `Controller` within the same scope, you can use the `Builder` component.
286299

287300
```tsx
288301
<Builder
289-
// specify the type to query
302+
// specify the class type to query
290303
source={CounterController}
291304
buildWhen={(prev, curr) => {
292305
// 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
307320
```tsx
308321
<Builder
309322
// passing an instance
310-
source={counterControllerInstance}
323+
source={counterInstance}
311324
buildWhen={(prev, curr) => true}
312325
>
313326
{(state: number) => {
@@ -327,7 +340,7 @@ import { useBuilder } from 'react-state-view-controller'
327340
// type
328341
const [state, controller] = useBuilder(CounterController, (prev, curr) => true)
329342
// or instance
330-
const state = useBuilder(counterControllerInstance)
343+
const state = useBuilder(counterInstance)
331344
```
332345

333346
### Selector
@@ -347,7 +360,7 @@ Usually, we don't need to watch for changes in the whole `State`, but rather jus
347360
It also support direct controller instance variant
348361

349362
```tsx
350-
<Selector source={multiCounterControllerInstance} selector={(state) => state.count5}>
363+
<Selector source={multiCounterInstance} selector={(state) => state.count5}>
351364
{(value) => {
352365
// render content based on selected value
353366
return <View></View>
@@ -361,9 +374,9 @@ Alternatively, we can use the `useSelector` hook
361374
import { useSelector } from 'react-state-view-controller'
362375

363376
// only trigger re-render when `state.count5` changed
364-
const [value, controller] = useSelector(CounterController, (state) => state.count5)
377+
const [value, controller] = useSelector(MultiCounterController, (state) => state.count5)
365378
// or the equivalent
366-
const value = useSelector(counterControllerInstance, (state) => state.count5)
379+
const value = useSelector(multiCounterInstance, (state) => state.count5)
367380
```
368381

369382
### Listener
@@ -389,7 +402,7 @@ The controller instance variant:
389402

390403
```tsx
391404
<Listener
392-
source={counterControllerInstance}
405+
source={counterInstance}
393406
listener={(state: number) => console.log(state)}
394407
listenWhen={(prev: number, current: number) => true}
395408
>
@@ -409,7 +422,7 @@ const controller = useListener(
409422
)
410423
// or
411424
const controller = useListener(
412-
counterControllerInstance,
425+
counterInstance,
413426
(state) => console.log(state), // callback when state changed
414427
(prev, curr) => true, // callback filter
415428
)
@@ -419,9 +432,9 @@ Note that this hook is not intended by default to cause re-render, it just simpl
419432

420433
## For other types
421434

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.
423436

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.
425438

426439
## Conclusion
427440

0 commit comments

Comments
 (0)