@cleanweb/oore 1.2.2-beta.0 → 2.0.0-alpha.13
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.
|
@@ -108,7 +108,7 @@ var ClassComponent = /** @class */ (function (_super) {
|
|
|
108
108
|
* // Render with `<Button.RC />`, or export RC to use the component in other files.
|
|
109
109
|
* export default Button.RC;
|
|
110
110
|
*/
|
|
111
|
-
ClassComponent.extract = function FC(_Component) {
|
|
111
|
+
ClassComponent.extract = function FC(_Component, properties) {
|
|
112
112
|
var Component = _Component !== null && _Component !== void 0 ? _Component : this;
|
|
113
113
|
var isClassComponentType = Component.prototype instanceof _a;
|
|
114
114
|
if (!isClassComponentType)
|
|
@@ -116,7 +116,7 @@ var ClassComponent = /** @class */ (function (_super) {
|
|
|
116
116
|
/*************************************
|
|
117
117
|
* Begin Function Component *
|
|
118
118
|
**************************************/
|
|
119
|
-
/** A class-based, React function component created with `@cleanweb/
|
|
119
|
+
/** A class-based, React function component created with `@cleanweb/oore`. {@link ClassComponent} */
|
|
120
120
|
var Wrapper = function (props) {
|
|
121
121
|
var instance = (0, instance_1.useInstance)(Component, props);
|
|
122
122
|
var template = instance.template, templateContext = instance.templateContext;
|
|
@@ -131,10 +131,10 @@ var ClassComponent = /** @class */ (function (_super) {
|
|
|
131
131
|
return template(templateContext);
|
|
132
132
|
};
|
|
133
133
|
/**************************************
|
|
134
|
-
*
|
|
134
|
+
* 👆🏼 End Function Component *
|
|
135
135
|
**************************************/
|
|
136
136
|
(0, function_name_1.setFunctionName)(Wrapper, "$".concat(Component.name, "$"));
|
|
137
|
-
return Wrapper;
|
|
137
|
+
return Object.assign(Wrapper, properties);
|
|
138
138
|
};
|
|
139
139
|
/** @see {@link ClassComponent.extract} */
|
|
140
140
|
ClassComponent.FC = _a.extract;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VoidFunctionComponent } from 'react';
|
|
2
2
|
import type { ClassComponent } from '..';
|
|
3
3
|
type BaseCCConstructor = typeof ClassComponent<object>;
|
|
4
|
-
export type Extractor = <TComponentClass extends BaseCCConstructor>(this: TComponentClass, Component?: TComponentClass) => VoidFunctionComponent<InstanceType<TComponentClass>['props']>;
|
|
4
|
+
export type Extractor = <TComponentClass extends BaseCCConstructor, TProperties extends {} = {}>(this: TComponentClass, Component?: TComponentClass | null, properties?: TProperties) => NonNullable<TProperties> & VoidFunctionComponent<InstanceType<TComponentClass>['props']>;
|
|
5
5
|
export {};
|
package/build/slots/hook.d.ts
CHANGED
|
@@ -10,10 +10,10 @@ export declare const getComponentSlotName: IGetSlotName;
|
|
|
10
10
|
*
|
|
11
11
|
* @returns A {@link TUseSlotsResult} array,
|
|
12
12
|
* which includes a `slotNodes` object that maps the keys from
|
|
13
|
-
* the predefined {@link
|
|
13
|
+
* the predefined {@link Caller.slots} object to the corresponding
|
|
14
14
|
* React node(s) that were rendered for that slot.
|
|
15
15
|
*
|
|
16
16
|
* @see {@link SlotComponent} for more on how to use the returned slot nodes.
|
|
17
17
|
*/
|
|
18
18
|
export declare const useSlots: IUseSlots;
|
|
19
|
-
export type { SlotNamedComponent, SlottedComponent, TSlotsRecord, PotentialSlotComponent, } from './types';
|
|
19
|
+
export type { SlotNamedComponent, Slotted as SlottedComponent, TSlotsRecord, PotentialSlotComponent, } from './types';
|
package/build/slots/hook.js
CHANGED
|
@@ -70,14 +70,14 @@ exports.getComponentSlotName = getComponentSlotName;
|
|
|
70
70
|
*
|
|
71
71
|
* @returns A {@link TUseSlotsResult} array,
|
|
72
72
|
* which includes a `slotNodes` object that maps the keys from
|
|
73
|
-
* the predefined {@link
|
|
73
|
+
* the predefined {@link Caller.slots} object to the corresponding
|
|
74
74
|
* React node(s) that were rendered for that slot.
|
|
75
75
|
*
|
|
76
76
|
* @see {@link SlotComponent} for more on how to use the returned slot nodes.
|
|
77
77
|
*/
|
|
78
|
-
var useSlots = function (children,
|
|
78
|
+
var useSlots = function (children, Caller) {
|
|
79
79
|
var slotsAliasLookup = (0, react_1.useMemo)(function () {
|
|
80
|
-
var entries = Object.entries(
|
|
80
|
+
var entries = Object.entries(Caller.slots);
|
|
81
81
|
var aliasLookup = {};
|
|
82
82
|
entries.forEach(function (_a) {
|
|
83
83
|
var alias = _a[0], RegisteredSlotComponent = _a[1];
|
|
@@ -89,13 +89,15 @@ var useSlots = function (children, slotComponents, requiredSlotAliases) {
|
|
|
89
89
|
aliasLookup[slotName] = alias;
|
|
90
90
|
});
|
|
91
91
|
return aliasLookup;
|
|
92
|
-
}, [
|
|
92
|
+
}, [Caller.slots]);
|
|
93
93
|
var result = (0, react_1.useMemo)(function () {
|
|
94
|
+
var _a;
|
|
94
95
|
var slotNodes = {};
|
|
95
96
|
var unmatchedChildren = [];
|
|
96
97
|
var invalidChildren = [];
|
|
97
|
-
var
|
|
98
|
+
var requiredSlotAliases = __spreadArray([], ((_a = Caller.requiredSlotAliases) !== null && _a !== void 0 ? _a : []), true);
|
|
98
99
|
react_1.default.Children.forEach(children, function (child) {
|
|
100
|
+
var _a;
|
|
99
101
|
if (!child) {
|
|
100
102
|
invalidChildren.push(child);
|
|
101
103
|
return;
|
|
@@ -106,6 +108,7 @@ var useSlots = function (children, slotComponents, requiredSlotAliases) {
|
|
|
106
108
|
return;
|
|
107
109
|
}
|
|
108
110
|
;
|
|
111
|
+
// @todo Check for fragment
|
|
109
112
|
if (!(0, exports.isElementChild)(child)) {
|
|
110
113
|
unmatchedChildren.push(child);
|
|
111
114
|
return;
|
|
@@ -114,9 +117,9 @@ var useSlots = function (children, slotComponents, requiredSlotAliases) {
|
|
|
114
117
|
var slotName = (0, exports.getComponentSlotName)(child.type, child);
|
|
115
118
|
return slotName ? slotsAliasLookup[slotName] : null;
|
|
116
119
|
})();
|
|
117
|
-
if (slotAlias && (typeof
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
+
if (slotAlias && (typeof Caller.slots[slotAlias] !== 'string')) {
|
|
121
|
+
if ((_a = Caller.slots[slotAlias]) === null || _a === void 0 ? void 0 : _a.isRequiredSlot) {
|
|
122
|
+
requiredSlotAliases.push(slotAlias);
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
if (slotAlias) {
|
|
@@ -130,7 +133,7 @@ var useSlots = function (children, slotComponents, requiredSlotAliases) {
|
|
|
130
133
|
else
|
|
131
134
|
unmatchedChildren.push(child);
|
|
132
135
|
});
|
|
133
|
-
|
|
136
|
+
requiredSlotAliases.forEach(function (slotAlias) {
|
|
134
137
|
if (!slotNodes[slotAlias]) {
|
|
135
138
|
(0, errors_1.throwDevError)("Missing required slot \"".concat(String(slotAlias), "\"."));
|
|
136
139
|
}
|
package/build/slots/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { ComponentMethods } from '../base';
|
|
2
|
+
import { ComponentLogic } from '../classy';
|
|
1
3
|
import type { ReactElement, ReactNode, JSXElementConstructor } from 'react';
|
|
2
|
-
export type TComponent = JSXElementConstructor<any
|
|
4
|
+
export type TComponent = JSXElementConstructor<any> | ComponentLogic | ComponentMethods;
|
|
3
5
|
export type TSlotName = keyof any;
|
|
4
6
|
export type TSlotAlias = keyof any;
|
|
5
7
|
/**
|
|
@@ -79,8 +81,9 @@ export type SlotComponent<TComponentArg extends TComponent = TComponent> = (Slot
|
|
|
79
81
|
* directly from the parent component itself,
|
|
80
82
|
* through an alias that is easy to remember.
|
|
81
83
|
*/
|
|
82
|
-
export type
|
|
83
|
-
|
|
84
|
+
export type Slotted<TComponentArg extends object = TComponent, TSlotAliasArg extends TSlotAlias = TSlotAlias, TSlotsRecordArg extends TSlotsRecord<TSlotAliasArg> = TSlotsRecord<TSlotAliasArg>> = TComponentArg & {
|
|
85
|
+
slots: TSlotsRecordArg;
|
|
86
|
+
requiredSlotAliases?: TSlotAliasArg[];
|
|
84
87
|
};
|
|
85
88
|
/**
|
|
86
89
|
* A record of slot aliases mapped to the corresponding `ReactNode`(s)
|
|
@@ -115,21 +118,6 @@ export interface IUseSlots {
|
|
|
115
118
|
* The nodes it contains will be categorized and
|
|
116
119
|
* grouped according to the predefined {@link slotComponents}.
|
|
117
120
|
*/
|
|
118
|
-
children: ReactNode,
|
|
119
|
-
/**
|
|
120
|
-
* A map of slot aliases to their corresponding {@link SlotComponent}.
|
|
121
|
-
* React nodes passed as children will be compared with the components
|
|
122
|
-
* in this record to generate the categorized `slotNodes` object.
|
|
123
|
-
*/
|
|
124
|
-
slotComponents: TSlotsRecord<TSlotAliasArg>,
|
|
125
|
-
/**
|
|
126
|
-
* An array of aliases to be treated as required. If a slot's
|
|
127
|
-
* alias is in this array, the slot is treated as required.
|
|
128
|
-
*
|
|
129
|
-
* If no ReactNode is found in {@link children | `children`}
|
|
130
|
-
* for a required slot, an error will be thrown in development.
|
|
131
|
-
* However in production this is softened to just a console warning.
|
|
132
|
-
*/
|
|
133
|
-
requiredSlotAliases?: TSlotAliasArg[]): TUseSlotsResult<TSlotAliasArg>;
|
|
121
|
+
children: ReactNode, Caller: Slotted): TUseSlotsResult<TSlotAliasArg>;
|
|
134
122
|
}
|
|
135
123
|
export type PotentialSlotComponent = string | SlotComponent | TComponent;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleanweb/oore",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.13",
|
|
4
4
|
"description": "A library of helpers for writing cleaner React function components with object-oriented patterns.",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": ">=
|
|
6
|
+
"node": ">=22"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"build",
|
package/README.old.md
DELETED
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
# Structured & Cleaner React Function Components
|
|
2
|
-
|
|
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 three main exported members.
|
|
5
|
-
|
|
6
|
-
> The following sections will use examples to demonstrate how each tool can be used. These example are intended to be read as a before and after comparison. They are all based on the same "before" code, which is shown below:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
### Our Sample React Function Component
|
|
10
|
-
```jsx
|
|
11
|
-
const Footer = (props) => {
|
|
12
|
-
const [state1, setState1] = useState(props.defaultValue);
|
|
13
|
-
const [state2, setState2] = useState();
|
|
14
|
-
const [label, setLabel] = useState('Click me');
|
|
15
|
-
const [submitted, setSubmitted] = useState(false);
|
|
16
|
-
|
|
17
|
-
const [store, updateStore] = useGlobalStore();
|
|
18
|
-
|
|
19
|
-
const { param } = props;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Required to run once *before* the component mounts.
|
|
23
|
-
const paragraphs = useMemo(() => getValue(param), [param]);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
/** @todo Use `useSyncExternalStore`. */
|
|
27
|
-
const subscribeToExternalDataSource = useCallback(() => {
|
|
28
|
-
externalDataSource.subscribe((data) => {
|
|
29
|
-
setLabel(data.label);
|
|
30
|
-
});
|
|
31
|
-
}, [setLabel]);
|
|
32
|
-
|
|
33
|
-
// Required to run once *after* the component mounts.
|
|
34
|
-
useEffect(subscribeToExternalDataSource, []);
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
const onWindowResize = () => {};
|
|
37
|
-
window.addEventListener('resize', onWindowResize);
|
|
38
|
-
|
|
39
|
-
return () => {
|
|
40
|
-
window.removeEventListener('resize', onWindowResize);
|
|
41
|
-
};
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
// Run *after* every render.
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
doSomething();
|
|
47
|
-
return () => {};
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
const submit = useCallback(() => {
|
|
51
|
-
sendData(state1, state2);
|
|
52
|
-
setSubmitted(true);
|
|
53
|
-
}, [state1]); // Notice how `state2` above could easily be stale by the time the callback runs.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Run before every render.
|
|
57
|
-
const text = `${label}, please.`;
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<footer>
|
|
61
|
-
{paragraphs ? paragraphs.map((copy) => (
|
|
62
|
-
<p>{copy}</p>
|
|
63
|
-
)) : null}
|
|
64
|
-
|
|
65
|
-
<button onClick={submit}>
|
|
66
|
-
{text}
|
|
67
|
-
</button>
|
|
68
|
-
</footer>
|
|
69
|
-
);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export default Footer;
|
|
73
|
-
// Or use in JSX as `<Footer />`.
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### A Cleaner State Management API
|
|
78
|
-
The `useCleanState` hook provides a cleaner API for working with state, particularly in components with a relatively high number of state variables. It allows you to manage multiple state variables as a single unit. You will likely find this cleaner to work with than having multiple local variables for each state. The example below demonstrates the use of this hook.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
### Extracting and Structuring Component Logic
|
|
82
|
-
The `useLogic` hook 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.
|
|
83
|
-
|
|
84
|
-
**Before**
|
|
85
|
-
[See the "before" code above](#our-sample-react-function-component).
|
|
86
|
-
|
|
87
|
-
**After**
|
|
88
|
-
```jsx
|
|
89
|
-
class FooterLogic {
|
|
90
|
-
static getInitialState = (props) => {
|
|
91
|
-
return {
|
|
92
|
-
state1: props.defaultValue,
|
|
93
|
-
state2: undefined,
|
|
94
|
-
label: 'Click me',
|
|
95
|
-
submitted: false,
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
subscribeToExternalDataSource = () => {
|
|
100
|
-
externalDataSource.subscribe((data) => {
|
|
101
|
-
this.state.label = data.label;
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
useHooks = () => {
|
|
106
|
-
const [store, updateStore] = useGlobalStore();
|
|
107
|
-
const { param } = this.props;
|
|
108
|
-
|
|
109
|
-
// Required to run once *before* the component mounts.
|
|
110
|
-
const paragraphs = useMemo(() => getValue(param), [param]);
|
|
111
|
-
|
|
112
|
-
// Required to run once *after* the component mounts.
|
|
113
|
-
useEffect(this.subscribeToExternalDataSource, []);
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
const onWindowResize = () => {};
|
|
116
|
-
window.addEventListener('resize', onWindowResize);
|
|
117
|
-
|
|
118
|
-
return () => {
|
|
119
|
-
window.removeEventListener('resize', onWindowResize);
|
|
120
|
-
};
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
|
-
// Run *after* every render.
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
doSomething();
|
|
126
|
-
return () => {};
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
store,
|
|
131
|
-
updateStore,
|
|
132
|
-
paragraphs,
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
submit = () => {
|
|
137
|
-
const { state1, state2 } = this.state;
|
|
138
|
-
sendData(state1, state2);
|
|
139
|
-
this.state.submitted = true;
|
|
140
|
-
}; // `state2` is never stale. All `state` and `props` references return the latest value. No extra steps required.
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Footer Template
|
|
144
|
-
const Footer = (props) => {
|
|
145
|
-
const self = useLogic(FooterLogic, props);
|
|
146
|
-
|
|
147
|
-
const { paragraphs } = self.hooks;
|
|
148
|
-
const { state } = self;
|
|
149
|
-
|
|
150
|
-
// Run before every render.
|
|
151
|
-
const text = `${state.label}, please.`;
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<footer>
|
|
155
|
-
{paragraphs ? paragraphs.map((copy) => (
|
|
156
|
-
<p>{copy}</p>
|
|
157
|
-
)) : null}
|
|
158
|
-
|
|
159
|
-
<button onClick={self.submit}>
|
|
160
|
-
{text}
|
|
161
|
-
</button>
|
|
162
|
-
</footer>
|
|
163
|
-
);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
export default Footer;
|
|
167
|
-
// Or use in JSX as `<Footer />`.
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
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 as well as the added functionality of the `useHooks` method.
|
|
171
|
-
|
|
172
|
-
> 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.
|
|
173
|
-
|
|
174
|
-
For a fuller discussion of how `useLogic` works, start at the [clean-state documentation](https://cleanjsweb.github.io/neat-react/clean-state/index).
|
|
175
|
-
For an API reference, see the [API reference](https://cleanjsweb.github.io/neat-react/logic/api).
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
### Working With Lifecycle, and Migrating From a React.Component Class to a Function Component
|
|
179
|
-
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`.
|
|
180
|
-
|
|
181
|
-
**Before**
|
|
182
|
-
[See the "before" code above](#our-sample-react-function-component).
|
|
183
|
-
|
|
184
|
-
**After**
|
|
185
|
-
```jsx
|
|
186
|
-
class Footer extends ClassComponent {
|
|
187
|
-
// Call the static method extract() to retrieve the renderer for your class.
|
|
188
|
-
// This is a function that you can render like any other function component.
|
|
189
|
-
// Each instance of the renderer in your component tree will create its own instance of the class.
|
|
190
|
-
static readonly RC = Button.extract();
|
|
191
|
-
|
|
192
|
-
static getInitialState = (props) => {
|
|
193
|
-
return {
|
|
194
|
-
state1: props.defaultValue,
|
|
195
|
-
state2: undefined,
|
|
196
|
-
label: 'Click me',
|
|
197
|
-
submitted: false,
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
useHooks = () => {
|
|
202
|
-
const [store, updateStore] = useGlobalStore();
|
|
203
|
-
return { store, updateStore };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/***************************
|
|
207
|
-
* New Lifecycle Methods *
|
|
208
|
-
***************************/
|
|
209
|
-
|
|
210
|
-
beforeMount = () => {
|
|
211
|
-
this.paragraphs = getValue();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Run after the component is mounted.
|
|
215
|
-
onMount = () => {
|
|
216
|
-
this.subscribeToExternalDataSource();
|
|
217
|
-
window.addEventListener('resize', this.onWindowResize);
|
|
218
|
-
|
|
219
|
-
// Return cleanup callback.
|
|
220
|
-
return () => {
|
|
221
|
-
window.removeEventListener('resize', this.onWindowResize);
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
beforeRender = () => {
|
|
226
|
-
this.text = `${label}, please.`;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Run after every render.
|
|
230
|
-
onRender = () => {
|
|
231
|
-
doSomething();
|
|
232
|
-
|
|
233
|
-
// Return cleanup callback.
|
|
234
|
-
return () => {};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
cleanUp = () => {
|
|
238
|
-
// Run some non-mount-related cleanup when the component dismounts.
|
|
239
|
-
// onMount (and onRender) returns its own cleanup function.
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/***************************
|
|
243
|
-
* [End] Lifecycle Methods *
|
|
244
|
-
***************************/
|
|
245
|
-
|
|
246
|
-
submit = () => {
|
|
247
|
-
// Methods are guaranteed to have access to the most recent state values.
|
|
248
|
-
const { state1, state2 } = this.state;
|
|
249
|
-
|
|
250
|
-
sendData(state1, state2);
|
|
251
|
-
|
|
252
|
-
// CleanState uses JavaScript's getters and setters, allowing you to assign state values directly.
|
|
253
|
-
// The effect is the same as if you called the setter function, which is available through `state.put.submitted(true)`.
|
|
254
|
-
this.state.submitted = true;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
onWindowResize = () => {};
|
|
258
|
-
|
|
259
|
-
subscribeToExternalDataSource = () => {
|
|
260
|
-
const unsubscribe = externalDataSource.subscribe((data) => {
|
|
261
|
-
this.state.label = data.label;
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
return unsubscribe;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** You can also separate out discreet chunks of your UI template. */
|
|
268
|
-
Paragraphs = () => {
|
|
269
|
-
if (!this.paragraphs) return null;
|
|
270
|
-
|
|
271
|
-
return this.paragraphs.map((content, index) => (
|
|
272
|
-
<p key={index}>
|
|
273
|
-
{content || this.state.label}
|
|
274
|
-
</p>
|
|
275
|
-
));
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/** Button Template */
|
|
279
|
-
template = () => {
|
|
280
|
-
const { Paragraphs, submit, state } = this;
|
|
281
|
-
|
|
282
|
-
return (
|
|
283
|
-
<Fragment>
|
|
284
|
-
<Paragraphs />
|
|
285
|
-
|
|
286
|
-
{/* You can access the setter functions returned from useState through the state.put object. */}
|
|
287
|
-
{/* This is more convenient than the assignment approach if you need to pass a setter as a callback. */}
|
|
288
|
-
{/* Use state.putMany to set multiple values at once. It works just like setState in React.Component classes. */}
|
|
289
|
-
{/* e.g state.inputValue = 'foo', or state.put.inputValue('foo'), or state.putMany({ inputValue: 'foo' }) */}
|
|
290
|
-
<CustomInput setValue={state.put.inputValue} />
|
|
291
|
-
|
|
292
|
-
<button onClick={submit}>
|
|
293
|
-
{this.text}
|
|
294
|
-
</button>
|
|
295
|
-
</Fragment>
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Can also be used directly in JSX as `<Button.RC />`.
|
|
301
|
-
export default Button.RC;
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
> 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.
|
|
305
|
-
|
|
306
|
-
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.
|
|
307
|
-
|
|
308
|
-
For a fuller discussion of how this works, start at the [`useInstance` documentation](https://cleanjsweb.github.io/neat-react/instance/index).
|
|
309
|
-
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).
|
|
310
|
-
|
|
311
|
-
### The `<Use>` Component
|
|
312
|
-
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).
|
|
313
|
-
|
|
314
|
-
```jsx
|
|
315
|
-
class Button extends React.Component {
|
|
316
|
-
handleGlobalStore = ([store, updateStore]) => {
|
|
317
|
-
this.setState({ userId: store.userId });
|
|
318
|
-
this.store = store;
|
|
319
|
-
this.updateStore = updateStore;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
UseHooks = () => {
|
|
323
|
-
return <>
|
|
324
|
-
<Use hook={useGlobalStore}
|
|
325
|
-
onUpdate={handleGlobalStore}
|
|
326
|
-
argumentsList={[]}
|
|
327
|
-
key="useGlobalStore"
|
|
328
|
-
/>
|
|
329
|
-
</>;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
render() {
|
|
333
|
-
const { UseHooks } = this;
|
|
334
|
-
|
|
335
|
-
return <>
|
|
336
|
-
<UseHooks />
|
|
337
|
-
|
|
338
|
-
<button>Click me</button>
|
|
339
|
-
</>;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
```
|