@cleanweb/oore 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +374 -0
  2. package/README.old.md +342 -0
  3. package/build/base/index.d.ts +3 -0
  4. package/build/base/index.js +19 -0
  5. package/build/base/merged-state.d.ts +20 -0
  6. package/build/base/merged-state.js +84 -0
  7. package/build/base/methods.d.ts +59 -0
  8. package/build/base/methods.js +100 -0
  9. package/build/base/state/class-types.d.ts +20 -0
  10. package/build/base/state/class-types.js +2 -0
  11. package/build/base/state/class.d.ts +16 -0
  12. package/build/base/state/class.js +100 -0
  13. package/build/base/state/hook-types.d.ts +32 -0
  14. package/build/base/state/hook-types.js +2 -0
  15. package/build/base/state/hooks.d.ts +12 -0
  16. package/build/base/state/hooks.js +45 -0
  17. package/build/base/state/index.d.ts +8 -0
  18. package/build/base/state/index.js +35 -0
  19. package/build/classy/class/index.d.ts +115 -0
  20. package/build/classy/class/index.js +161 -0
  21. package/build/classy/class/types/extractor.d.ts +5 -0
  22. package/build/classy/class/types/extractor.js +2 -0
  23. package/build/classy/class/utils/function-name.d.ts +2 -0
  24. package/build/classy/class/utils/function-name.js +17 -0
  25. package/build/classy/index.d.ts +3 -0
  26. package/build/classy/index.js +19 -0
  27. package/build/classy/instance/index.d.ts +144 -0
  28. package/build/classy/instance/index.js +202 -0
  29. package/build/classy/instance/mount-callbacks.d.ts +5 -0
  30. package/build/classy/instance/mount-callbacks.js +30 -0
  31. package/build/classy/instance/types/hook.d.ts +13 -0
  32. package/build/classy/instance/types/hook.js +2 -0
  33. package/build/classy/logic/index.d.ts +116 -0
  34. package/build/classy/logic/index.js +128 -0
  35. package/build/classy/logic/types/hook.d.ts +16 -0
  36. package/build/classy/logic/types/hook.js +2 -0
  37. package/build/docs-src/api/base-classes.d.ts +3 -0
  38. package/build/docs-src/api/base-classes.js +9 -0
  39. package/build/docs-src/api/index.d.ts +13 -0
  40. package/build/docs-src/api/index.js +44 -0
  41. package/build/docs-src/api/references.d.ts +5 -0
  42. package/build/docs-src/api/references.js +31 -0
  43. package/build/globals.d.ts +84 -0
  44. package/build/globals.js +4 -0
  45. package/build/helpers/index.d.ts +13 -0
  46. package/build/helpers/index.js +31 -0
  47. package/build/helpers/mount-state.d.ts +5 -0
  48. package/build/helpers/mount-state.js +25 -0
  49. package/build/helpers/rerender.d.ts +5 -0
  50. package/build/helpers/rerender.js +29 -0
  51. package/build/helpers/type-guards.d.ts +1 -0
  52. package/build/helpers/type-guards.js +8 -0
  53. package/build/helpers/use-component/index.d.ts +6 -0
  54. package/build/helpers/use-component/index.js +17 -0
  55. package/build/helpers/use-component/types.d.ts +22 -0
  56. package/build/helpers/use-component/types.js +2 -0
  57. package/build/index.d.ts +4 -0
  58. package/build/index.js +19 -0
  59. package/build/tsconfig.json +49 -0
  60. package/package.json +87 -0
package/README.md ADDED
@@ -0,0 +1,374 @@
1
+ # Object Oriented Programming for React
2
+ This package provides a number of tools for creating React function components with object-oriented code. With Oore, you can avoid common errors, and write complex components that are cleaner, better structured, and eassier to read & understand.
3
+
4
+ Oore is written in Typescript and all exports are fully typed.
5
+
6
+ ## Usage
7
+ ### Clean State
8
+ The `useCleanState` hook provides a clean API for working with multiple state variables in a unified way. The example below demonstrates how to use it.
9
+
10
+ ```jsx
11
+ const initialState = {
12
+ label: 'Click me',
13
+ clicked: false,
14
+ inputValue: {},
15
+ };
16
+ // or
17
+ const getInitialState = (props) => {
18
+ return {
19
+ label: props.label,
20
+ clicked: false,
21
+ inputValue: {},
22
+ };
23
+ };
24
+
25
+ const Button = (props) => {
26
+ const state = useCleanState(initialState);
27
+ // or
28
+ const state = useCleanState(getInitialState, props);
29
+
30
+
31
+ const onClick = useCallback(() => {
32
+ state.clicked = true;
33
+ // or
34
+ state.putMany({
35
+ label: 'Loading',
36
+ clicked: true,
37
+ });
38
+ // or
39
+ state.put.clicked(true);
40
+ // or
41
+ state.put.clicked((oldClicked) => !oldClicked);
42
+ }, []);
43
+
44
+ return <>
45
+ <CustomInput setValue={state.put.inputValue}>
46
+
47
+ <button onClick={onClick}>
48
+ {state.label}
49
+ </button>
50
+ </>;
51
+ }
52
+ ```
53
+
54
+ > **Note:** You can call `useCleanState` multiple times in the same component, allowing you to group related state values into separate objects.
55
+
56
+ > **Note:** Each top-level key in your initial state object gets a separate call to `React.useState`, and `state.put[key]()` is a proxy for the setter function returned from `useState`. So using this hook is fundamentally the same as calling `useState` directly for each value. What `useCleanState` provides is a way to unify those values and a convenient API for updating them.
57
+
58
+ [Read the `useCleanState` API docs](https://cleanjsweb.github.io/neat-react/functions/API.useCleanState.html) for more details.
59
+
60
+ ### Methods
61
+ The `useMethods` hook lets you manage the closures that your component uses in a separate class, keeping the body of the component clean and easier to read. With `useMethods`, your functions are not recreated on every render. Yet, every method of your component is guaranteed to always have access to the latest props and state without the need for a dependencty array.
62
+
63
+ ```jsx
64
+ class ButtonMethods {
65
+ submit = () => {
66
+ const { state1, state2 } = this.state;
67
+ sendData(state1, state2);
68
+ this.state.submitted = true;
69
+ }
70
+
71
+ doSomething = () => {
72
+ console.table(this.props);
73
+ }
74
+ }
75
+
76
+ const initialState = {
77
+ value1: undefined,
78
+ value2: null,
79
+ label: 'Click me',
80
+ submitted: false,
81
+ }
82
+
83
+ const Button = (props) => {
84
+ const state = useCleanState(initialState);
85
+ const methods = useMethods(ButtonMethods, state, props);
86
+
87
+ useEffect(methods.doSomething, []);
88
+
89
+ return (
90
+ <button onClick={methods.submit}>
91
+ {state.label}
92
+ </button>
93
+ );
94
+ }
95
+ ```
96
+
97
+ `useMethods` only accepts a single state object. So if you are using multiple calls to `useCleanState`, you may have to group them into a single object when calling `useMethods`.
98
+
99
+ ```jsx
100
+ const getDefaultValues = (props) => ({/* ... */});
101
+
102
+ const Button = (props) => {
103
+ const formValues = useCleanState(getDefaultValues, props);
104
+ const apiData = useCleanState({});
105
+
106
+ const multiState = { formValues, apiData };
107
+ const methods = useMethods(ButtonMethods, multiState, props);
108
+
109
+ // ...
110
+ }
111
+ ```
112
+
113
+ [Read the `useMethods` API docs](https://cleanjsweb.github.io/neat-react/functions/API.useMethods.html) for more details.
114
+
115
+ <small>Discussion: [Reasoning behind `useMethods`](https://cleanjsweb.github.io/neat-react/documents/Methods_Hook.html).</small>
116
+
117
+ ### Logic
118
+ The `useLogic` hook is an expansion of [`useMethods`](#methods), with the aim of being a more holistic solution. It combines the functionality of [`useCleanState`](#clean-state) and [`useMethods`](#methods).
119
+
120
+ In addition, it allows you to externalize _all_ of your component's logic, not just closures and state. Essentially, this means being able to call hooks from within the class, rather than having to do so within the component body.
121
+
122
+ ```jsx
123
+ class ButtonLogic {
124
+ static getInitialState = () => {
125
+ return {
126
+ value1: undefined,
127
+ value2: null,
128
+ label: 'Click me',
129
+ submitted: false,
130
+ };
131
+ }
132
+
133
+ submit = async () => {
134
+ const { value1, value2 } = this.state;
135
+ await sendData(value1, value2);
136
+ this.state.submitted = true;
137
+ }
138
+
139
+ doSomething = () => {
140
+ // ...
141
+ }
142
+
143
+ useHooks = () => {
144
+ const { param } = this.props;
145
+
146
+ useEffect(this.doSomething, []);
147
+
148
+ const memoizedValue = useMemo(() => getValue(param), [param]);
149
+ const value2 = useCustomHook();
150
+
151
+ return {
152
+ memoizedValue,
153
+ value2,
154
+ };
155
+ }
156
+ }
157
+
158
+ // Button Template
159
+ const Button = (props) => {
160
+ const { state, hooks, ...methods } = useLogic(ButtonLogic, props);
161
+
162
+ return <>
163
+ <p>{hooks.memoizedValue}</p>
164
+ <button onClick={methods.submit}>
165
+ {state.label}
166
+ </button>
167
+ </>;
168
+ }
169
+ ```
170
+
171
+ [Read the `useLogic` docs](https://cleanjsweb.github.io/neat-react/documents/Logic_Hook.html).
172
+
173
+
174
+ ### Lifecycle (`useInstance`)
175
+ The `useInstance` hook provides a simple approach for working with your component's lifecycle. It includes all the features of [`useLogic`](#logic), and adds special lifecycle methods.
176
+
177
+ This gives you a declarative way to run certain code at specific stages of your component's life time. You will likely find this to be less error prone and much easier to reason about than the imperative approach of using React's hooks directly.
178
+
179
+ ```jsx
180
+ /** Button Component Class. */
181
+ class ButtonCC extends ComponentInstance {
182
+ // ...
183
+ // Static method(s), same as useLogic...
184
+ // ...
185
+
186
+
187
+ /* Lifecycle Methods */
188
+
189
+ beforeMount = () => {
190
+ // Runs before the component is first rendered.
191
+ // This is implemented using useMemo.
192
+ }
193
+
194
+ onMount = () => {
195
+ // Runs after the component is first rendered.
196
+ // Same as `useEffect(() => {}, []);`
197
+
198
+ return () => {
199
+ // Required clean up function must be returned.
200
+ // Return an empty function if you have no cleanup.
201
+ };
202
+ }
203
+
204
+ beforeRender = () => {
205
+ // Runs before every single render.
206
+ // Same as code placed before the return statement in a function component.
207
+
208
+ // Example: Generate display values from state and props,
209
+ // and return them for use in your JSX template.
210
+ const displayValue2 = this.hooks.memoizedValue + this.state.value2;
211
+
212
+ // The optional returned object will be available
213
+ // on the created instance as `instance.templateContext`
214
+ return {
215
+ intro: `Hello, ${this.props.name}`,
216
+ displayValue2,
217
+ };
218
+
219
+ // PS: For any expensive logic, you should wrap it
220
+ // in useMemo and move it into the useHooks method instead.
221
+ }
222
+
223
+ onRender = () => {
224
+ // Runs after every single render.
225
+ // Same as `useEffect(() => {});`
226
+
227
+ return () => {
228
+ // Required clean up function must be returned.
229
+ // Return an empty function if you have no cleanup.
230
+ };
231
+ }
232
+
233
+ cleanUp = () => {
234
+ // Runs when the component is unmounted.
235
+ // Similar to the function returned by `onMount`.
236
+ }
237
+
238
+ /* [End] Lifecycle Methods */
239
+
240
+
241
+ // ...
242
+ // Other instance methods, same as useLogic...
243
+ // ...
244
+ }
245
+
246
+ // Button Template
247
+ const Button = (props) => {
248
+ const self = useInstance(ButtonCC, props);
249
+ const ctx = self.templateContext;
250
+
251
+ return <>
252
+ <p>{ctx.intro}</p>
253
+ <button onClick={self.submit}>
254
+ {self.state.label}
255
+ </button>
256
+ </>;
257
+ }
258
+ ```
259
+
260
+ [Read the `useInstance` API docs](https://cleanjsweb.github.io/neat-react/functions/API.useInstance.html) for more details.
261
+
262
+ <small>For a lengthier discussion on the reasoning behind the `useInstance` hook, see the [`useInstance` discussion doc](https://cleanjsweb.github.io/neat-react/documents/Instance_Hook.html).
263
+
264
+
265
+ ### Class Component
266
+ With `useInstance`, pretty much every aspect of your component is now part of the class, except for the JSX template. The `ClassComponent` class takes that final step and provides a fully integrated class-based React component.
267
+
268
+ If you're currently maintaining older components written with the old `React.Component` class and you would like to rewrite them as function components with hooks, porting them to `ClassComponent` will _significantly_ simplify and speed up the migration process. You can access all the latest React features, without changing the overall structure of your existing component classes.
269
+
270
+
271
+ ```jsx
272
+ class Button extends ClassComponent {
273
+ /** See the description of the `RC` property below this example. */
274
+ static RC = Button.extract();
275
+ // Or...
276
+ static RC = Button.FC();
277
+ // Or, using `this`, which refers to the class itself
278
+ // when the static keyword is present...
279
+ static RC = this.extract();
280
+ // Or...
281
+ static RC = this.FC();
282
+
283
+ // ...
284
+ // Other static and instance members, same as useInstance...
285
+ // ...
286
+
287
+
288
+ beforeRender = () => {
289
+ const displayValue2 = this.hooks.memoizedValue + this.state.value2;
290
+
291
+ return {
292
+ intro: `Hello, ${this.props.name}`,
293
+ displayValue2,
294
+ };
295
+ }
296
+
297
+ /**
298
+ * Button Template.
299
+ * @param ctx - The `templateContext` object returned in `beforeRender`.
300
+ * Will be `undefined` if nothing is returned by `beforeRender`.
301
+ */
302
+ template = (ctx) => (
303
+ <section>
304
+ <p>{ctx.intro}</p>
305
+
306
+ <button onClick={this.submit}>
307
+ {this.state.label}
308
+ </button>
309
+ </section>
310
+ );
311
+ }
312
+
313
+ export default Button.RC;
314
+ // Or render directly with `<Button.RC />`.
315
+ ```
316
+
317
+ Every class derived from the base `ClassComponent` is not itself a React component. Instead, it has a static `extract()` method (also aliased as `FC()` for "Function Component") which returns a function component that can be rendered like any other React component. Each instance of this function component mounted in the React tree creates it's own separate instance of your `ClassComponent` class.
318
+
319
+ To make it easier to use the class component directly, you should create a static property that holds the function component returned by `extract`. The recommended convention is to use the name `RC` (for "React Component"). Such a class can then easily be rendered as JSX by writing `<MyComponent.RC />`.
320
+
321
+ [Read the `ClassComponent` API docs](https://cleanjsweb.github.io/neat-react/classes/API.ClassComponent.html) for more details.
322
+
323
+ <small>For a discussion on how this works, and a comparison with React's older `React.Component` class, see the [`ClassComponent` discussion doc](https://cleanjsweb.github.io/neat-react/documents/Class_Component.html).</small>
324
+
325
+ ### Other Exports
326
+
327
+ #### The `<Use>` Component
328
+ If you simply want to use hooks in your `React.Component` class without having to rewrite anything, this package also exports a `<Use>` component that helps you achieve this easily. Here's how to use it.
329
+
330
+ ```jsx
331
+ class Button extends React.Component {
332
+ syncGlobalStore = ([store, updateStore]) => {
333
+ if (this.state.userId !== store.userId) {
334
+ this.setState({ userId: store.userId });
335
+ }
336
+ this.store = store;
337
+ this.updateStore = updateStore;
338
+ }
339
+
340
+ UseHooks = () => {
341
+ return <>
342
+ <Use hook={useGlobalStore}
343
+ onUpdate={syncGlobalStore}
344
+ argumentsList={[]}
345
+ key="useGlobalStore"
346
+ />
347
+ </>;
348
+ }
349
+
350
+ render() {
351
+ const { UseHooks } = this;
352
+
353
+ return <>
354
+ <UseHooks />
355
+
356
+ <button>Click me</button>
357
+ </>;
358
+ }
359
+ }
360
+ ```
361
+
362
+ The provided hook is called with the `argumentsList` array passed in (the array is spread, so each item in the list is a separate argument). The return value from the hook is passed on to the `onUpdate` callback. So you can use this to update your component's state and trigger a rerender when something changes.
363
+
364
+ #### Merged State
365
+ This package also exports a `useMergedState` hook, which provides all the same features as `useCleanState`, but with a slightly different implementation.
366
+
367
+ The `useCleanState` hook is designed to exactly mirror how function components are usually written: a separate `React.useState` call for each distinct value. `useMergedState` takes a simpler approach by making just one `React.useState` call for the entire `initialState` object. Functionally though, the effect is probably the same.
368
+
369
+ It is recommended that you use `useCleanState` instead of this since that implementation is truer to how `React.useState` is commonly used. `useMergedState` may be removed in future versions.
370
+
371
+ ### Issues
372
+ If you observe an issue or bug, please report it by creating an issue on the [Oore repo on GitHub](https://github.com/cleanjsweb/oore/issues).
373
+
374
+