@cleanweb/react 1.1.1-beta.9 → 2.0.1-beta.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 (64) hide show
  1. package/README.md +242 -187
  2. package/README.old.md +342 -0
  3. package/build/base/merged-state.d.ts +1 -0
  4. package/build/base/merged-state.js +3 -2
  5. package/build/base/methods.d.ts +39 -6
  6. package/build/base/methods.js +45 -80
  7. package/build/base/state/class-types.d.ts +17 -0
  8. package/build/base/state/class-types.js +2 -0
  9. package/build/base/state/class.d.ts +14 -0
  10. package/build/base/{state.js → state/class.js} +4 -51
  11. package/build/base/state/hook-types.d.ts +11 -0
  12. package/build/base/state/hook-types.js +2 -0
  13. package/build/base/state/hooks.d.ts +11 -0
  14. package/build/base/state/hooks.js +57 -0
  15. package/build/base/state/index.d.ts +4 -0
  16. package/build/base/state/index.js +9 -0
  17. package/build/classy/class/index.d.ts +102 -0
  18. package/build/classy/class/index.js +147 -0
  19. package/build/classy/class/types/class/instance.d.ts +12 -0
  20. package/build/classy/class/types/class/instance.js +2 -0
  21. package/build/classy/class/types/class/static.d.ts +9 -0
  22. package/build/classy/class/types/class/static.js +12 -0
  23. package/build/classy/class/types/extractor.d.ts +6 -0
  24. package/build/classy/class/types/extractor.js +2 -0
  25. package/build/classy/class/utils/function-name.d.ts +2 -0
  26. package/build/classy/class/utils/function-name.js +17 -0
  27. package/build/classy/class/utils/rerender.d.ts +1 -0
  28. package/build/classy/class/utils/rerender.js +11 -0
  29. package/build/classy/class/utils/use-component/index.d.ts +6 -0
  30. package/build/classy/class/utils/use-component/index.js +17 -0
  31. package/build/classy/class/utils/use-component/types.d.ts +22 -0
  32. package/build/classy/class/utils/use-component/types.js +2 -0
  33. package/build/classy/instance/index.d.ts +94 -0
  34. package/build/classy/{instance.js → instance/index.js} +58 -38
  35. package/build/classy/instance/mount-callbacks.d.ts +4 -0
  36. package/build/classy/instance/mount-callbacks.js +30 -0
  37. package/build/classy/instance/types/hook.d.ts +16 -0
  38. package/build/classy/instance/types/hook.js +2 -0
  39. package/build/classy/instance/types/instance.d.ts +11 -0
  40. package/build/classy/instance/types/instance.js +2 -0
  41. package/build/classy/instance/types/static.d.ts +11 -0
  42. package/build/classy/instance/types/static.js +17 -0
  43. package/build/classy/logic/index.d.ts +42 -0
  44. package/build/classy/logic/index.js +122 -0
  45. package/build/classy/logic/types/hook.d.ts +24 -0
  46. package/build/classy/logic/types/hook.js +2 -0
  47. package/build/classy/logic/types/instance.d.ts +18 -0
  48. package/build/classy/logic/types/instance.js +2 -0
  49. package/build/classy/logic/types/static.d.ts +17 -0
  50. package/build/classy/logic/types/static.js +2 -0
  51. package/build/globals.d.ts +23 -33
  52. package/build/globals.js +2 -20
  53. package/build/index.d.ts +1 -1
  54. package/build/index.js +2 -1
  55. package/build/tsconfig.json +3 -0
  56. package/package.json +8 -6
  57. package/build/base/state.d.ts +0 -31
  58. package/build/classy/class.d.ts +0 -24
  59. package/build/classy/class.js +0 -132
  60. package/build/classy/instance.d.ts +0 -61
  61. package/build/classy/logic.d.ts +0 -20
  62. package/build/classy/logic.js +0 -82
  63. package/build/globals.private.d.ts +0 -44
  64. package/build/globals.private.js +0 -34
package/README.md CHANGED
@@ -1,76 +1,153 @@
1
- # Structured & Cleaner React Function Components
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.
2
3
 
3
- ## Quick Start
4
- This package provides a suite of tools for writing cleaner React function components. It is particularly useful for larger components with lots of state variables and multiple closure functions that need to access those variables. The most likely use cases will use one of the two main exported members.
4
+ Oore is written in Typescript and all exports are fully typed.
5
5
 
6
- ### Extracting and Structuring Component Logic
7
- The `useLogic` allows you to write your component's logic outside the function component's body, and helps you keep them all better organized. It also provides a much cleaner API for working with multiple state variables. Here's what a function component looks like with the `useLogic` hook.
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.
8
9
 
9
- **Before**
10
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
+
11
25
  const Button = (props) => {
12
- const { param } = 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
+ ```
13
53
 
14
- const [state1, setState1] = useState();
15
- const [state2, setState2] = useState();
16
- const [label, setLabel] = useState('Click me');
17
- const [submitted, setSubmitted] = useState(false);
54
+ > **Note:** You can call `useCleanState` multiple times in the same component, allowing you to group related state values into separate objects.
18
55
 
19
- const memoizedValue = useMemo(() => getValue(param), [param]);
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.
20
57
 
21
- const subscribeToExternalDataSource = useCallback(() => {
22
- externalDataSource.subscribe((data) => {
23
- setLabel(data.label);
24
- });
25
- }, [setLabel]);
58
+ [Read the `useCleanState` docs]() for more details.
26
59
 
27
- useEffect(subscribeToExternalDataSource, []);
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.
28
62
 
29
- const submit = useCallback(() => {
63
+ ```jsx
64
+ class ButtonMethods {
65
+ submit = () => {
66
+ const { state1, state2 } = this.state;
30
67
  sendData(state1, state2);
31
- setSubmitted(true);
32
- }, [state1]); // Notice how `state2` above could easily be stale by the time the callback runs.
68
+ this.state.submitted = true;
69
+ }
33
70
 
34
- return <>
35
- <p>{memoizedValue}</p>
36
- <button onClick={submit}>
37
- {label}
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}
38
92
  </button>
39
- </>;
93
+ );
40
94
  }
41
95
  ```
42
96
 
43
- **After**
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` docs]() for more details.
114
+
115
+ ### Logic
116
+ The `useLogic` hook is an expansion of `useMethods`, with the aim of being a more holistic solution. It combines the functionality of `useCleanState` and `useMethods`. 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.
117
+
44
118
  ```jsx
45
119
  class ButtonLogic {
46
120
  static getInitialState = () => {
47
121
  return {
48
- state1: undefined,
49
- state2: null,
122
+ value1: undefined,
123
+ value2: null,
50
124
  label: 'Click me',
51
125
  submitted: false,
52
126
  };
53
127
  }
54
128
 
55
- submit = () => {
56
- const { state1, state2 } = this.state;
57
- sendData(state1, state2);
129
+ submit = async () => {
130
+ const { value1, value2 } = this.state;
131
+ await sendData(value1, value2);
58
132
  this.state.submitted = true;
59
133
  }
60
134
 
61
- subscribeToExternalDataSource = () => {
62
- externalDataSource.subscribe((data) => {
63
- this.state.label = data.label;
64
- });
135
+ doSomething = () => {
136
+ // ...
65
137
  }
66
138
 
67
139
  useHooks = () => {
68
140
  const { param } = this.props;
69
141
 
70
- useEffect(this.subscribeToExternalDataSource, []);
142
+ useEffect(this.doSomething, []);
143
+
71
144
  const memoizedValue = useMemo(() => getValue(param), [param]);
145
+ const value2 = useCustomHook();
72
146
 
73
- return { memoizedValue };
147
+ return {
148
+ memoizedValue,
149
+ value2,
150
+ };
74
151
  }
75
152
  }
76
153
 
@@ -87,203 +164,164 @@ const Button = (props) => {
87
164
  }
88
165
  ```
89
166
 
90
- The `useLogic` hook combines the functionality of two base hooks which can also be used directly. They are [`useCleanState`](https://cleanjsweb.github.io/neat-react/clean-state/index) and [`useMethods`](https://cleanjsweb.github.io/neat-react/methods/index). `useCleanState` can be used independently if you only want a cleaner state management API. `useMethods` is designed to be used together with `useCleanState`, but rather than calling both individually, you may find it more convenient to use `useLogic`, which combines both and also adds additional functionality.
167
+ [Read the `useLogic` docs]() for more details.
91
168
 
92
- > It is possible to have multiple calls to `useLogic` in the same component. This allows your function component template to consume state and logic from multiple sources, or it can simply be used to group distinct pieces of related logic into separate classes.
93
-
94
- For a fuller discussion of how `useLogic` works, start at the [clean-state documentation](https://cleanjsweb.github.io/neat-react/clean-state/index).
95
- For an API reference, see the [API reference](https://cleanjsweb.github.io/neat-react/logic/api).
169
+ ### Lifecycle (`useInstance`)
170
+ The `useInstance` hook provides a simple approach for working with your components lifecycle. It includes all the features of [`useLogic`](#logic), and adds special lifecycle methods. 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.
96
171
 
172
+ ```jsx
173
+ /** Button Component Class. */
174
+ class ButtonCC extends ComponentInstance {
175
+ // ...
176
+ // Static method(s), same as useLogic...
177
+ // ...
97
178
 
98
- ### Working With Lifecycle, and Migrating From a React.Component Class to a Function Component
99
- In addition to having cleaner and more structured component logic, you can also simplify the process of working with your component's lifecycle with the final two exported members. The `useInstance` hook builds on the functionality of `useLogic` and adds lifecyle methods to the class. This means the class can now be thought of as truly representing a single instance of a React component. The `ClassComponent` class extends this to its fullest by allowing you to write the function component itself as a method within the class, and removing the need to explicitly call `useInstance`.
100
179
 
101
- **Before**
102
- ```jsx
103
- const Button = (props) => {
104
- const [state1, setState1] = useState(props.defaultValue);
105
- const [state2, setState2] = useState();
106
- const [label, setLabel] = useState('Click me');
107
- const [submitted, setSubmitted] = useState(false);
108
- const [store, updateStore] = useGlobalStore();
109
-
110
- // Required to run once *before* the component mounts.
111
- const memoizedValue = useMemo(() => getValue(), []);
112
-
113
- // Required to run once *after* the component mounts.
114
- useEffect(() => {
115
- const unsubscribe = externalDataSource.subscribe((data) => {
116
- setLabel(data.label);
117
- });
180
+ /* Lifecycle Methods */
118
181
 
119
- const onWindowResize = () => {};
182
+ beforeMount = () => {
183
+ // Runs before the component is first rendered.
184
+ // This is implemented using useMemo.
185
+ }
120
186
 
121
- window.addEventListener('resize', onWindowResize);
187
+ onMount = () => {
188
+ // Runs after the component is first rendered.
189
+ // Same as `useEffect(() => {}, []);`
122
190
 
123
191
  return () => {
124
- unsubscribe();
125
- window.removeEventListener('resize', onWindowResize);
192
+ // Required clean up function must be returned.
193
+ // Return an empty function if you have no cleanup.
126
194
  };
127
- }, []);
128
-
129
- // Run *after* every render.
130
- useEffect(() => {
131
- doSomething();
132
- return () => {};
133
- })
134
-
135
- const submit = useCallback(() => {
136
- sendData(state1, state2);
137
- setSubmitted(true);
138
- }, [state1]);
139
-
140
- // Run before every render.
141
- const text = `${label}, please.`;
195
+ }
142
196
 
143
- return <>
144
- {memoizedValue ? memoizedValue.map((copy) => (
145
- <p>{copy}</p>
146
- )) : null}
147
- <button onClick={submit}>
148
- {text}
149
- </button>
150
- </>;
151
- }
197
+ beforeRender = () => {
198
+ // Runs before every single render.
199
+ // Same as code placed before the return statement in a function component.
152
200
 
153
- export default Button;
154
- ```
201
+ // Example: Generate display values from state and props,
202
+ // and return them for use in your JSX template.
203
+ const displayValue2 = this.hooks.memoizedValue + this.state.value2;
155
204
 
156
- **After**
157
- ```jsx
158
- class Button extends ClassComponent {
159
- static getInitialState = (props) => {
205
+ // The optional returned object will be available
206
+ // on the created instance as `instance.templateContext`
160
207
  return {
161
- state1: props.defaultValue,
162
- state2: null,
163
- label: 'Click me',
164
- submitted: false,
208
+ intro: `Hello, ${this.props.name}`,
209
+ displayValue2,
165
210
  };
166
- }
167
211
 
168
- useHooks = () => {
169
- const [store, updateStore] = useGlobalStore();
170
- return { store, updateStore };
171
- }
172
-
173
- /***************************
174
- * New Lifecycle Methods *
175
- ***************************/
176
-
177
- beforeMount = () => {
178
- this.memoizedValue = getValue();
212
+ // PS: For any expensive logic, you should wrap it
213
+ // in useMemo and move it into the useHooks method instead.
179
214
  }
180
215
 
181
- // Run after the component is mounted.
182
- onMount = () => {
183
- const unsubscribe = this.subscribeToExternalDataSource();
184
- window.addEventListener('resize', this.onWindowResize);
216
+ onRender = () => {
217
+ // Runs after every single render.
218
+ // Same as `useEffect(() => {});`
185
219
 
186
- // Return cleanup callback.
187
220
  return () => {
188
- unsubscribe();
189
- window.removeEventListener('resize', this.onWindowResize);
221
+ // Required clean up function must be returned.
222
+ // Return an empty function if you have no cleanup.
190
223
  };
191
224
  }
192
225
 
193
- beforeRender = () => {
194
- this.text = `${label}, please.`;
226
+ cleanUp = () => {
227
+ // Runs when the component is unmounted.
228
+ // Similar to the function returned by `onMount`.
195
229
  }
196
230
 
197
- // Run after every render.
198
- onRender = () => {
199
- doSomething();
231
+ /* [End] Lifecycle Methods */
200
232
 
201
- // Return cleanup callback.
202
- return () => {};
203
- }
204
233
 
205
- cleanUp = () => {
206
- // Run some non-mount-related cleanup when the component dismounts.
207
- // onMount (and onRender) returns its own cleanup function.
208
- }
209
-
210
- /***************************
211
- * [End] Lifecycle Methods *
212
- ***************************/
234
+ // ...
235
+ // Other instance methods, same as useLogic...
236
+ // ...
237
+ }
213
238
 
214
- submit = () => {
215
- // Methods are guaranteed to have access to the most recent state values,
216
- // without any delicate hoops to jump through.
217
- const { state1, state2 } = this.state;
239
+ // Button Template
240
+ const Button = (props) => {
241
+ const self = useInstance(ButtonCC, props);
242
+ const ctx = self.templateContext;
218
243
 
219
- sendData(state1, state2);
244
+ return <>
245
+ <p>{ctx.intro}</p>
246
+ <button onClick={self.submit}>
247
+ {self.state.label}
248
+ </button>
249
+ </>;
250
+ }
251
+ ```
220
252
 
221
- // CleanState uses JavaScript's getters and setters, allowing you to assign state values directly.
222
- // The effect is the same as if you called the setter function, which is available through `state.put.submitted(true)`.
223
- this.state.submitted = true;
224
- }
253
+ [Read the `useInstance` docs]() for more details.
225
254
 
226
- onWindowResize = () => {
227
- ;
228
- }
255
+ ### Class Component
256
+ 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.
229
257
 
230
- subscribeToExternalDataSource = () => {
231
- const unsubscribe = externalDataSource.subscribe((data) => {
232
- this.state.label = data.label;
233
- });
258
+ 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.
234
259
 
235
- return unsubscribe;
236
- }
237
260
 
238
- /** You can also separate out discreet chunks of your UI template. */
239
- Paragraphs = () => {
240
- if (!this.memoizedValue) return null;
261
+ ```jsx
262
+ class Button extends ClassComponent {
263
+ /** See the description of the `RC` property below this example. */
264
+ static RC = Button.extract();
265
+ // Or...
266
+ static RC = Button.FC();
267
+ // Or, using `this`, which refers to the class itself
268
+ // when the static keyword is present...
269
+ static RC = this.extract();
270
+ // Or...
271
+ static RC = this.FC();
241
272
 
242
- return this.memoizedValue.map((content, index) => (
243
- <p key={index}>
244
- {content || this.state.label}
245
- </p>
246
- ));
247
- }
273
+ // ...
274
+ // Other static and instance members, same as useInstance...
275
+ // ...
248
276
 
249
- /** Button Template */
250
- Render = () => {
251
- const { Paragraphs, submit, state } = this;
252
277
 
253
- return <>
254
- <Paragraphs />
278
+ beforeRender = () => {
279
+ const displayValue2 = this.hooks.memoizedValue + this.state.value2;
255
280
 
256
- {/* You can access the setter functions returned from useState through the state.put object. */}
257
- {/* This is more convenient than the assignment approach if you need to pass a setter as a callback. */}
258
- {/* Use state.putMany to set multiple values at once. It works just like setState in React.Component classes. */}
259
- {/* e.g state.inputValue = 'foo', or state.put.inputValue('foo'), or state.putMany({ inputValue: 'foo' }) */}
260
- <CustomInput setValue={state.put.inputValue}>
281
+ return {
282
+ intro: `Hello, ${this.props.name}`,
283
+ displayValue2,
284
+ };
285
+ }
261
286
 
262
- <button onClick={submit}>
263
- {this.text}
287
+ /**
288
+ * Button Template.
289
+ * @param ctx - The `templateContext` object returned in `beforeRender`.
290
+ * Will be `undefined` if nothing is returned by `beforeRender`.
291
+ */
292
+ template = (ctx) => (
293
+ <section>
294
+ <p>{ctx.intro}</p>
295
+
296
+ <button onClick={this.submit}>
297
+ {this.state.label}
264
298
  </button>
265
- </>;
266
- }
299
+ </section>
300
+ );
267
301
  }
268
302
 
269
- // Call the static method FC() to get a function component that you can render like any other function component.
270
- export default Button.FC();
303
+ export default Button.RC;
304
+ // Or render directly with `<Button.RC />`.
271
305
  ```
272
306
 
273
- > If you would like to keep the actual function component separate and call `useInstance` directly, see the [`useInstance` docs](https://cleanjsweb.github.io/neat-react/instance/index) for more details and examples.
307
+ 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. 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 />`.
308
+
309
+ [Read the `ClassComponent` docs]() for more details.
274
310
 
275
- At its core, any component you write with `ClassComponent` is still just a React function component, with some supporting logic around it. This has the added advantage of making it significantly easier to migrate class components written with `React.Component` to the newer hooks-based function components, while still maintaining the overall structure of a class component, and the advantages that the class component approach provided.
276
311
 
277
- For a fuller discussion of how this works, start at the [`useInstance` documentation](https://cleanjsweb.github.io/neat-react/instance/index).
278
- For more details on the lifecycle methods and other API reference, see the [`ClassComponent` API docs](https://cleanjsweb.github.io/neat-react/class-component/api).
312
+ ### Other Exports
279
313
 
280
- ### The `<Use>` Component
281
- If you only want to use hooks in your `React.Component` class without having to refactor anything, use the [`Use` component](https://cleanjsweb.github.io/neat-react/class-component/index#the-use-component).
314
+ #### The `<Use>` Component
315
+ 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.
282
316
 
283
317
  ```jsx
318
+ import { useGlobalStore } from '@/hooks/store';
319
+
284
320
  class Button extends React.Component {
285
- handleGlobalStore = ([store, updateStore]) => {
286
- this.setState({ userId: store.userId });
321
+ syncGlobalStore = ([store, updateStore]) => {
322
+ if (this.state.userId !== store.userId) {
323
+ this.setState({ userId: store.userId });
324
+ }
287
325
  this.store = store;
288
326
  this.updateStore = updateStore;
289
327
  }
@@ -291,7 +329,7 @@ class Button extends React.Component {
291
329
  UseHooks = () => {
292
330
  return <>
293
331
  <Use hook={useGlobalStore}
294
- onUpdate={handleGlobalStore}
332
+ onUpdate={syncGlobalStore}
295
333
  argumentsList={[]}
296
334
  key="useGlobalStore"
297
335
  />
@@ -309,3 +347,20 @@ class Button extends React.Component {
309
347
  }
310
348
  }
311
349
  ```
350
+
351
+ 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.
352
+
353
+ #### Merged State
354
+ This package also exports a `useMergedState` hook, which provides all the same features as `useCleanState`, but with a slightly different implementation.
355
+
356
+ 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.
357
+
358
+ 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.
359
+
360
+ ### Issues
361
+ If you observe an issue or bug, please report it by creating an issue on the [Oore repo on GitHub]().
362
+
363
+ #### Known Issues
364
+ Methods on your component classes may not be updated as expected during HMR. So fully refreshing the page may sometimes be required while developing with Oore. A fix for this is being investigated.
365
+
366
+