@twobirds/microcomponents 0.0.14 → 0.0.16
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.
- package/README.md +3 -1
- package/assets/documentation/tB_react.md +82 -247
- package/package.json +1 -1
- package/public/documentation/tB_react.md +72 -208
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ npm i @mySampleLib/Button
|
|
|
16
16
|
|
|
17
17
|
Imagine your micro components to be organized like a "node_modules" directory that is attached to a DOM node.
|
|
18
18
|
|
|
19
|
+
The MC() class acts as a **Javascript repository on a DOM node** and as a **selector and communication tool across the DOM**.
|
|
20
|
+
|
|
19
21
|
**On the client side** MCs reside inside DOM nodes in a "_mc" property, or inside each other recursively. Usually they provide a direct interface to their inner code.
|
|
20
22
|
|
|
21
23
|
**On the server side** MCs can be used to organize your code, too.
|
|
@@ -98,6 +100,6 @@ the _mc attribute and properties are the only interference with the DOM, so ever
|
|
|
98
100
|
## Contact
|
|
99
101
|
Feel free to contact me for any questions or inquiries:
|
|
100
102
|
|
|
101
|
-
email [Frank Thürigen](mailto:fthuerigen@gmail.com?subject=Micro%20Components)
|
|
103
|
+
email [Frank Thürigen](mailto:fthuerigen@gmail.com?subject=Micro%20Components)
|
|
102
104
|
|
|
103
105
|
And again, you can [fund this](https://gofund.me/076c614c).
|
|
@@ -8,32 +8,6 @@ Please mind I have a trained session with a backlog. So you wont get the same an
|
|
|
8
8
|
|
|
9
9
|
***( Cut outs and comments are in parentheses...)***
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
## Disclaimer:
|
|
13
|
-
|
|
14
|
-
The answers partly reflect on the twoBirds framework instead of Micro Components. twoBirds converts all props into convenience observables automatically, which MCs don't do.
|
|
15
|
-
|
|
16
|
-
The code ChatGPT generated first isn't quite right therefore. Also since I am not a react specialist I cannot guarantee for that part. In general, this is close enough though.
|
|
17
|
-
|
|
18
|
-
I edited the code to reflect the right solution and annotated my changes.
|
|
19
|
-
|
|
20
|
-
I put that into the first answer and asked ChatGPT again.
|
|
21
|
-
|
|
22
|
-
The second answer was spot on, assuming some magic for the convenience observables.
|
|
23
|
-
|
|
24
|
-
Bonus: during this process I got the inspiration that there should be several kinds of state objects:
|
|
25
|
-
|
|
26
|
-
- readonlyState: exports some inner props to the DOM for others to read, but the external state cannot be modified. Not observable.
|
|
27
|
-
- observableState: observable readonlyState. You can attach callbacks that fire on changes.
|
|
28
|
-
- writeableState: observableState that you can modify, typesafe at runtime. Inner methods will do further work probably.
|
|
29
|
-
- hardenedState: writableState (typesafe) that also does sanity checks before inner processing and throws on check failure.
|
|
30
|
-
|
|
31
|
-
This will be a complete public state model in the DOM.
|
|
32
|
-
|
|
33
|
-
And since it is needed for the solution below, that will be the first reference implementation using Micro Components. I hope the React world will be happy.
|
|
34
|
-
|
|
35
|
-
Working with AI can be wonderful...
|
|
36
|
-
|
|
37
11
|
## Start Session
|
|
38
12
|
|
|
39
13
|
Me:
|
|
@@ -64,13 +38,13 @@ React uses Context and hooks (like useState and useEffect) to manage data flow a
|
|
|
64
38
|
Your approach leans on manual wiring for connecting data across microcomponents, avoiding the abstraction of hooks and allowing direct, observable-driven updates. This can make the flow of data more transparent and easier to trace in complex applications, whereas hooks can sometimes add complexity or obscurity when trying to understand data dependencies.
|
|
65
39
|
|
|
66
40
|
### Component Reusability and Scaling:
|
|
67
|
-
Both React and
|
|
41
|
+
Both React and Micro Components support component-based architecture.
|
|
68
42
|
|
|
69
|
-
However,
|
|
43
|
+
However, Micro Components allows multiple microcomponents on a single DOM element, and each microcomponent is responsible for specific tasks, making scaling in large applications more modular and straightforward. React encourages separation at the component level, whereas Micro Components does so at the microcomponent level, with direct chaining possible between components.
|
|
70
44
|
|
|
71
45
|
(...)
|
|
72
46
|
|
|
73
|
-
Each of these aspects underlines
|
|
47
|
+
Each of these aspects underlines Micro Components’ focus on minimalism, modularity, and developer control. These are different from React's abstraction and tooling-heavy approach, making your framework potentially more transparent and lightweight for specific use cases.
|
|
74
48
|
|
|
75
49
|
Would you like to dive into any of these differences in more detail?
|
|
76
50
|
|
|
@@ -91,23 +65,20 @@ React requires passing data through each level of components in the tree via pro
|
|
|
91
65
|
|
|
92
66
|
(...)
|
|
93
67
|
|
|
94
|
-
In contrast, your
|
|
68
|
+
In contrast, your Micro Components microcomponents have an alternative approach that makes it easier to pass properties throughout the DOM tree:
|
|
95
69
|
|
|
96
70
|
### Direct Microcomponent Selectors:
|
|
97
|
-
|
|
71
|
+
Micro Components includes selector methods (like .parents() and .descendants()) that allow components to access any microcomponent within the tree without explicit data passing. This capability enables deeply nested components to retrieve properties from higher-level components directly, bypassing the need to route data through intermediary components.
|
|
98
72
|
|
|
99
|
-
|
|
100
|
-
Since twoBirds uses observables, properties from a root component can be shared across components by observing changes directly. When the observable changes, all relevant components are notified and updated automatically. This is a natural data propagation method that doesn’t require explicit context or external state management.
|
|
101
|
-
|
|
102
|
-
***( ...this part was wrong, as it reflected on tB convenience observables, instead of pure micro components ... )***
|
|
73
|
+
(...)
|
|
103
74
|
|
|
104
75
|
### Manual Wiring for Control:
|
|
105
76
|
|
|
106
|
-
**Because
|
|
77
|
+
**Because Micro Components rely on manual wiring, you can set up specific connections between components as needed.** This gives you fine-grained control over where and how data flows through the app without involving a top-level state or intermediary components, making it more scalable for larger apps without the need for a Context API or state library.
|
|
107
78
|
|
|
108
79
|
(...)
|
|
109
80
|
|
|
110
|
-
This structure offers a highly flexible, context-free solution for deep property sharing, making it an efficient alternative to prop drilling or global state libraries in many scenarios. Would you like more details on how
|
|
81
|
+
This structure offers a highly flexible, context-free solution for deep property sharing, making it an efficient alternative to prop drilling or global state libraries in many scenarios. Would you like more details on how Micro Components selectors work with observables to achieve this?
|
|
111
82
|
|
|
112
83
|
|
|
113
84
|
## Generate Code
|
|
@@ -115,249 +86,113 @@ This structure offers a highly flexible, context-free solution for deep property
|
|
|
115
86
|
Me:
|
|
116
87
|
**Can you give an example of 2 react components that interact with each other using micro components? Give me the code of both components, using microomponents to exchange data**
|
|
117
88
|
|
|
118
|
-
***(
|
|
119
|
-
|
|
120
|
-
***( ChatGPTs errors were based on the assumption that MCs behave like twoBirds micro components, which turn properties in convenience observables automatically. These can be accessed like simple properties, but still work as observables. )***
|
|
121
|
-
|
|
122
|
-
Skip to [Final Version](#final-version) if you dont want to see my intermediate corrections.
|
|
89
|
+
***( it took some further hinting, but in the end ChatGPT came up with this: )***
|
|
123
90
|
|
|
124
91
|
|
|
125
92
|
ChatGPT:
|
|
126
93
|
|
|
127
|
-
Here
|
|
128
|
-
|
|
129
|
-
In this scenario, we’ll have two React components:
|
|
130
|
-
|
|
131
|
-
ParentComponent: Contains a microcomponent managing shared state.
|
|
132
|
-
ChildComponent: Retrieves and observes updates to that shared state through the microcomponent system.
|
|
133
|
-
|
|
134
|
-
The Micro Component framework will handle state observability, allowing ChildComponent to respond to changes in ParentComponent.
|
|
94
|
+
Here's the updated code:
|
|
95
|
+
Parent Micro Component
|
|
135
96
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
First, let’s create a microcomponent to hold shared data. This MC will be instantiated in ParentComponent and observed in ChildComponent.
|
|
97
|
+
This component will receive a target DOM node and set a value that propagates down to its descendants:
|
|
139
98
|
|
|
140
99
|
```javascript
|
|
141
100
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
super(target);
|
|
148
|
-
|
|
149
|
-
// Define an observable property for shared data
|
|
150
|
-
// this part is wrong: ChatGPT assumed this property is a twoBirds convenience observable property, which MCs dont do automatically.
|
|
151
|
-
// lets for now assume it is
|
|
152
|
-
this.sharedData = "Initial data from Parent"; // this in fact is assumed to be an observable in tB, as any property defined in the constructor.
|
|
153
|
-
// Also, this observable property would be runtime type safe, BTW.
|
|
154
|
-
// This is important for public MCs, as they expose a PUBLIC interface to internal properties for EVERYBODY to use at runtime. So:
|
|
155
|
-
// document.getElementById("parent-root")._mc.sharedState.sharedData = 'My hilarious string';
|
|
156
|
-
// ... this will trigger the callbacks associated with it
|
|
157
|
-
// MCs ACT AS INTERFACES!
|
|
158
|
-
}
|
|
101
|
+
// Parent Micro Component
|
|
102
|
+
class ParentMC extends MC {
|
|
103
|
+
constructor(targetNode) {
|
|
104
|
+
super(targetNode); // Receive and pass the target DOM node to the MC constructor
|
|
105
|
+
}
|
|
159
106
|
|
|
107
|
+
set value(newValue) {
|
|
108
|
+
// Send value to all descendants named "targetMC"
|
|
109
|
+
this.descendants("targetMC").forEach(descendant => descendant.data = newValue);
|
|
110
|
+
}
|
|
160
111
|
}
|
|
161
|
-
|
|
162
|
-
export default SharedStateMC;
|
|
163
112
|
```
|
|
164
113
|
|
|
165
|
-
|
|
114
|
+
Child Micro Component
|
|
166
115
|
|
|
167
|
-
|
|
116
|
+
This component has a data setter that updates the internal React state in each child:
|
|
168
117
|
|
|
169
118
|
```javascript
|
|
170
119
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// Attach SharedStateMC to the main component div
|
|
178
|
-
const parentElement = document.getElementById("parent-root");
|
|
179
|
-
const sharedState = MC.add( parentElement, 'sharedState', new SharedStateMC( parentElement ) );
|
|
180
|
-
|
|
181
|
-
// Simulate updating data after 3 seconds
|
|
182
|
-
// changing a tB convenience observable really is as easy as this, also it is type-safe at runtime
|
|
183
|
-
setTimeout(() => {
|
|
184
|
-
sharedState.sharedData = "Updated data from Parent";
|
|
185
|
-
}, 3000);
|
|
186
|
-
}, []);
|
|
187
|
-
|
|
188
|
-
return (
|
|
189
|
-
<div id="parent-root">
|
|
190
|
-
<h1>Parent Component</h1>
|
|
191
|
-
<p>Check ChildComponent for updated data.</p>
|
|
192
|
-
<ChildComponent />
|
|
193
|
-
</div>
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export default ParentComponent;
|
|
198
|
-
```
|
|
120
|
+
// Child Micro Component
|
|
121
|
+
class ChildMC extends MC {
|
|
122
|
+
constructor(targetNode, setReactState) {
|
|
123
|
+
super(targetNode); // Receive the target DOM node to be managed by MC
|
|
124
|
+
this.setReactState = setReactState;
|
|
125
|
+
}
|
|
199
126
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
|
|
206
|
-
// ChildComponent.js
|
|
207
|
-
import React, { useEffect, useState } from 'react';
|
|
208
|
-
|
|
209
|
-
function ChildComponent() {
|
|
210
|
-
const [data, setData] = useState("");
|
|
211
|
-
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
// Access the microcomponent instance on the parent's DOM element
|
|
214
|
-
const parentElement = document.getElementById("parent-root");
|
|
215
|
-
const sharedStateMC = parentElement._mc.sharedState;
|
|
216
|
-
|
|
217
|
-
// Initial data load
|
|
218
|
-
setData(sharedStateMC.sharedData);
|
|
219
|
-
|
|
220
|
-
// Observe changes to shared data in SharedStateMC
|
|
221
|
-
// this isn't quite right, for reasons mentioned above, but close
|
|
222
|
-
sharedStateMC.observe((newData) => {
|
|
223
|
-
setData(newData);
|
|
224
|
-
});
|
|
225
|
-
}, []);
|
|
226
|
-
|
|
227
|
-
return (
|
|
228
|
-
<div>
|
|
229
|
-
<h2>Child Component</h2>
|
|
230
|
-
<p>Data from Parent: {data}</p>
|
|
231
|
-
</div>
|
|
232
|
-
);
|
|
127
|
+
set data(newValue) {
|
|
128
|
+
this.setReactState(newValue); // Update React state when data is set
|
|
129
|
+
}
|
|
233
130
|
}
|
|
234
|
-
|
|
235
|
-
export default ChildComponent;
|
|
236
131
|
```
|
|
237
132
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
In ParentComponent: An instance of SharedStateMC is created and attached to the main DOM element with an ID parent-root.
|
|
241
|
-
After 3 seconds, it updates sharedData to a new value.
|
|
242
|
-
|
|
243
|
-
In ChildComponent: We access the SharedStateMC instance via the _mc property of the parent element and observe sharedData.
|
|
244
|
-
When sharedData changes, ChildComponent re-renders with the updated data.
|
|
245
|
-
|
|
246
|
-
### Explanation
|
|
247
|
-
|
|
248
|
-
Observable Property: sharedData is an observable property within SharedStateMC.
|
|
249
|
-
This allows the ChildComponent to subscribe and react to changes without using React's props or context.
|
|
250
|
-
Direct DOM Access:
|
|
251
|
-
The ChildComponent directly accesses ParentComponent's microcomponent via document.getElementById("parent-root")._mc.
|
|
252
|
-
|
|
253
|
-
This pattern effectively mimics cross-component communication in React but using the microcomponents as an intermediary for data sharing.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
## Final Version
|
|
257
|
-
|
|
258
|
-
Me:
|
|
259
|
-
**assume this revised markup... can you spot the difference? ( My feedback code from above was added )**
|
|
260
|
-
|
|
261
|
-
ChatGPT:
|
|
262
|
-
|
|
263
|
-
You’ve provided a revised example that incorporates microcomponents with clearer runtime type safety and public interfaces. Let’s review the differences and corrections made in this version:
|
|
133
|
+
React Components Using Micro Components
|
|
264
134
|
|
|
265
|
-
|
|
135
|
+
The following React setup attaches ParentMC and ChildMC to their respective DOM elements, using the parent DOM node to instantiate the micro components.
|
|
136
|
+
Parent Component
|
|
266
137
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
Final Revised Code
|
|
270
|
-
SharedStateMC.js
|
|
271
|
-
|
|
272
|
-
```javascript
|
|
273
|
-
import { MC } from '@twobirds/microcomponents';
|
|
274
|
-
|
|
275
|
-
class SharedStateMC extends MC {
|
|
276
|
-
constructor(target) {
|
|
277
|
-
super(target);
|
|
278
|
-
// Define an observable property for shared data with runtime type safety
|
|
279
|
-
this.sharedData = "Initial data from Parent";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export default SharedStateMC;
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
ParentComponent.js
|
|
138
|
+
The parent component attaches an instance of ParentMC to the DOM node and sets its value based on inputValue.
|
|
288
139
|
|
|
289
140
|
```javascript
|
|
290
|
-
import React, { useEffect } from 'react';
|
|
291
|
-
import SharedStateMC from './SharedStateMC';
|
|
292
|
-
import { MC } from '@twobirds/microcomponents';
|
|
293
|
-
|
|
294
|
-
function ParentComponent() {
|
|
295
|
-
useEffect(() => {
|
|
296
|
-
const parentElement = document.getElementById("parent-root");
|
|
297
|
-
|
|
298
|
-
// Attach SharedStateMC instance to parent element with 'sharedState' key
|
|
299
|
-
const sharedState = MC.add(parentElement, 'sharedState', new SharedStateMC(parentElement));
|
|
300
|
-
|
|
301
|
-
// Updating shared data after 3 seconds with observable property syntax
|
|
302
|
-
setTimeout(() => {
|
|
303
|
-
sharedState.sharedData = "Updated data from Parent";
|
|
304
|
-
}, 3000);
|
|
305
|
-
}, []);
|
|
306
|
-
|
|
307
|
-
return (
|
|
308
|
-
<div id="parent-root">
|
|
309
|
-
<h1>Parent Component</h1>
|
|
310
|
-
<p>Check ChildComponent for updated data.</p>
|
|
311
|
-
<ChildComponent />
|
|
312
|
-
</div>
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
141
|
|
|
316
|
-
|
|
142
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
143
|
+
import { MC } from '@Micro Components/microcomponents';
|
|
144
|
+
|
|
145
|
+
const ParentComponent = () => {
|
|
146
|
+
const [inputValue, setInputValue] = useState("");
|
|
147
|
+
const parentRef = useRef(null);
|
|
148
|
+
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const parentMC = new ParentMC(parentRef.current); // Pass the DOM node to ParentMC
|
|
151
|
+
MC.add(parentRef.current, 'parentMC', parentMC);
|
|
152
|
+
|
|
153
|
+
// Update value in ParentMC whenever inputValue changes
|
|
154
|
+
parentMC.value = inputValue;
|
|
155
|
+
}, [inputValue]);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<div ref={parentRef}>
|
|
159
|
+
<input
|
|
160
|
+
type="text"
|
|
161
|
+
value={inputValue}
|
|
162
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
163
|
+
placeholder="Type to send data to children"
|
|
164
|
+
/>
|
|
165
|
+
<ChildComponent />
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
};
|
|
317
169
|
```
|
|
318
170
|
|
|
171
|
+
Child Component
|
|
319
172
|
|
|
320
|
-
|
|
173
|
+
The child component attaches an instance of ChildMC, which updates its state whenever data is set.
|
|
321
174
|
|
|
322
175
|
```javascript
|
|
323
|
-
import React, { useEffect, useState } from 'react';
|
|
324
|
-
|
|
325
|
-
function ChildComponent() {
|
|
326
|
-
const [data, setData] = useState("");
|
|
327
|
-
|
|
328
|
-
useEffect(() => {
|
|
329
|
-
const parentElement = document.getElementById("parent-root");
|
|
330
|
-
const sharedStateMC = parentElement._mc.sharedState;
|
|
331
|
-
|
|
332
|
-
// Initial data load
|
|
333
|
-
setData(sharedStateMC.sharedData);
|
|
334
|
-
|
|
335
|
-
// Observing updates to sharedData property
|
|
336
|
-
sharedStateMC.sharedData.observe((newData) => {
|
|
337
|
-
setData(newData);
|
|
338
|
-
});
|
|
339
|
-
}, []);
|
|
340
|
-
|
|
341
|
-
return (
|
|
342
|
-
<div>
|
|
343
|
-
<h2>Child Component</h2>
|
|
344
|
-
<p>Data from Parent: {data}</p>
|
|
345
|
-
</div>
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
176
|
|
|
349
|
-
|
|
177
|
+
const ChildComponent = () => {
|
|
178
|
+
const [childData, setChildData] = useState("");
|
|
179
|
+
const childRef = useRef(null);
|
|
180
|
+
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
const childMC = new ChildMC(childRef.current, setChildData); // Pass DOM node and state setter to ChildMC
|
|
183
|
+
MC.add(childRef.current, 'targetMC', childMC);
|
|
184
|
+
}, []);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<div ref={childRef}>
|
|
188
|
+
<h3>Received Data:</h3>
|
|
189
|
+
<p>{childData}</p>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
};
|
|
350
193
|
```
|
|
351
194
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
Observables: The sharedData property in SharedStateMC is now noted to be observable and accessible as a public interface.
|
|
355
|
-
|
|
356
|
-
Microcomponent Attaching: Using MC.add clarifies the attachment of the SharedStateMC instance to parentElement.
|
|
357
|
-
|
|
358
|
-
Runtime Type Safety: The property remains observable, allowing callbacks on sharedData in ChildComponent.
|
|
359
|
-
|
|
360
|
-
This implementation accurately reflects the design principles and interfaces for twoBirds MCs, allowing cross-component data sharing while ensuring type safety and a clean observable setup.
|
|
195
|
+
***( ... I cannot tell about the react part, but the micro coomponent code is spot on. Like WLAN on the DOM :-) )***
|
|
361
196
|
|
|
362
197
|
|
|
363
198
|
**ChatGPT can produce errors. Check the information.**
|
package/package.json
CHANGED
|
@@ -115,249 +115,113 @@ This structure offers a highly flexible, context-free solution for deep property
|
|
|
115
115
|
Me:
|
|
116
116
|
**Can you give an example of 2 react components that interact with each other using micro components? Give me the code of both components, using microomponents to exchange data**
|
|
117
117
|
|
|
118
|
-
***(
|
|
119
|
-
|
|
120
|
-
***( ChatGPTs errors were based on the assumption that MCs behave like twoBirds micro components, which turn properties in convenience observables automatically. These can be accessed like simple properties, but still work as observables. )***
|
|
121
|
-
|
|
122
|
-
Skip to [Final Version](#final-version) if you dont want to see my intermediate corrections.
|
|
118
|
+
***( it took some further hinting, but in the end ChatGPT came up with this: )***
|
|
123
119
|
|
|
124
120
|
|
|
125
121
|
ChatGPT:
|
|
126
122
|
|
|
127
|
-
Here
|
|
128
|
-
|
|
129
|
-
In this scenario, we’ll have two React components:
|
|
130
|
-
|
|
131
|
-
ParentComponent: Contains a microcomponent managing shared state.
|
|
132
|
-
ChildComponent: Retrieves and observes updates to that shared state through the microcomponent system.
|
|
133
|
-
|
|
134
|
-
The Micro Component framework will handle state observability, allowing ChildComponent to respond to changes in ParentComponent.
|
|
135
|
-
|
|
136
|
-
### Step 1: Set Up Micro Components for State Sharing
|
|
123
|
+
Here's the updated code:
|
|
124
|
+
Parent Micro Component
|
|
137
125
|
|
|
138
|
-
|
|
126
|
+
This component will receive a target DOM node and set a value that propagates down to its descendants:
|
|
139
127
|
|
|
140
128
|
```javascript
|
|
141
129
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
super(target);
|
|
148
|
-
|
|
149
|
-
// Define an observable property for shared data
|
|
150
|
-
// this part is wrong: ChatGPT assumed this property is a twoBirds convenience observable property, which MCs dont do automatically.
|
|
151
|
-
// lets for now assume it is
|
|
152
|
-
this.sharedData = "Initial data from Parent"; // this in fact is assumed to be an observable in tB, as any property defined in the constructor.
|
|
153
|
-
// Also, this observable property would be runtime type safe, BTW.
|
|
154
|
-
// This is important for public MCs, as they expose a PUBLIC interface to internal properties for EVERYBODY to use at runtime. So:
|
|
155
|
-
// document.getElementById("parent-root")._mc.sharedState.sharedData = 'My hilarious string';
|
|
156
|
-
// ... this will trigger the callbacks associated with it
|
|
157
|
-
// MCs ACT AS INTERFACES!
|
|
158
|
-
}
|
|
130
|
+
// Parent Micro Component
|
|
131
|
+
class ParentMC extends MC {
|
|
132
|
+
constructor(targetNode) {
|
|
133
|
+
super(targetNode); // Receive and pass the target DOM node to the MC constructor
|
|
134
|
+
}
|
|
159
135
|
|
|
136
|
+
set value(newValue) {
|
|
137
|
+
// Send value to all descendants named "targetMC"
|
|
138
|
+
this.descendants("targetMC").forEach(descendant => descendant.data = newValue);
|
|
139
|
+
}
|
|
160
140
|
}
|
|
161
|
-
|
|
162
|
-
export default SharedStateMC;
|
|
163
141
|
```
|
|
164
142
|
|
|
165
|
-
|
|
143
|
+
Child Micro Component
|
|
166
144
|
|
|
167
|
-
|
|
145
|
+
This component has a data setter that updates the internal React state in each child:
|
|
168
146
|
|
|
169
147
|
```javascript
|
|
170
148
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// Attach SharedStateMC to the main component div
|
|
178
|
-
const parentElement = document.getElementById("parent-root");
|
|
179
|
-
const sharedState = MC.add( parentElement, 'sharedState', new SharedStateMC( parentElement ) );
|
|
180
|
-
|
|
181
|
-
// Simulate updating data after 3 seconds
|
|
182
|
-
// changing a tB convenience observable really is as easy as this, also it is type-safe at runtime
|
|
183
|
-
setTimeout(() => {
|
|
184
|
-
sharedState.sharedData = "Updated data from Parent";
|
|
185
|
-
}, 3000);
|
|
186
|
-
}, []);
|
|
187
|
-
|
|
188
|
-
return (
|
|
189
|
-
<div id="parent-root">
|
|
190
|
-
<h1>Parent Component</h1>
|
|
191
|
-
<p>Check ChildComponent for updated data.</p>
|
|
192
|
-
<ChildComponent />
|
|
193
|
-
</div>
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export default ParentComponent;
|
|
198
|
-
```
|
|
149
|
+
// Child Micro Component
|
|
150
|
+
class ChildMC extends MC {
|
|
151
|
+
constructor(targetNode, setReactState) {
|
|
152
|
+
super(targetNode); // Receive the target DOM node to be managed by MC
|
|
153
|
+
this.setReactState = setReactState;
|
|
154
|
+
}
|
|
199
155
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
|
|
206
|
-
// ChildComponent.js
|
|
207
|
-
import React, { useEffect, useState } from 'react';
|
|
208
|
-
|
|
209
|
-
function ChildComponent() {
|
|
210
|
-
const [data, setData] = useState("");
|
|
211
|
-
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
// Access the microcomponent instance on the parent's DOM element
|
|
214
|
-
const parentElement = document.getElementById("parent-root");
|
|
215
|
-
const sharedStateMC = parentElement._mc.sharedState;
|
|
216
|
-
|
|
217
|
-
// Initial data load
|
|
218
|
-
setData(sharedStateMC.sharedData);
|
|
219
|
-
|
|
220
|
-
// Observe changes to shared data in SharedStateMC
|
|
221
|
-
// this isn't quite right, for reasons mentioned above, but close
|
|
222
|
-
sharedStateMC.observe((newData) => {
|
|
223
|
-
setData(newData);
|
|
224
|
-
});
|
|
225
|
-
}, []);
|
|
226
|
-
|
|
227
|
-
return (
|
|
228
|
-
<div>
|
|
229
|
-
<h2>Child Component</h2>
|
|
230
|
-
<p>Data from Parent: {data}</p>
|
|
231
|
-
</div>
|
|
232
|
-
);
|
|
156
|
+
set data(newValue) {
|
|
157
|
+
this.setReactState(newValue); // Update React state when data is set
|
|
158
|
+
}
|
|
233
159
|
}
|
|
234
|
-
|
|
235
|
-
export default ChildComponent;
|
|
236
160
|
```
|
|
237
161
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
In ParentComponent: An instance of SharedStateMC is created and attached to the main DOM element with an ID parent-root.
|
|
241
|
-
After 3 seconds, it updates sharedData to a new value.
|
|
162
|
+
React Components Using Micro Components
|
|
242
163
|
|
|
243
|
-
|
|
244
|
-
|
|
164
|
+
The following React setup attaches ParentMC and ChildMC to their respective DOM elements, using the parent DOM node to instantiate the micro components.
|
|
165
|
+
Parent Component
|
|
245
166
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Observable Property: sharedData is an observable property within SharedStateMC.
|
|
249
|
-
This allows the ChildComponent to subscribe and react to changes without using React's props or context.
|
|
250
|
-
Direct DOM Access:
|
|
251
|
-
The ChildComponent directly accesses ParentComponent's microcomponent via document.getElementById("parent-root")._mc.
|
|
252
|
-
|
|
253
|
-
This pattern effectively mimics cross-component communication in React but using the microcomponents as an intermediary for data sharing.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
## Final Version
|
|
257
|
-
|
|
258
|
-
Me:
|
|
259
|
-
**assume this revised markup... can you spot the difference? ( My feedback code from above was added )**
|
|
260
|
-
|
|
261
|
-
ChatGPT:
|
|
262
|
-
|
|
263
|
-
You’ve provided a revised example that incorporates microcomponents with clearer runtime type safety and public interfaces. Let’s review the differences and corrections made in this version:
|
|
264
|
-
|
|
265
|
-
***( ChatGPT explains my changes )***
|
|
266
|
-
|
|
267
|
-
Here’s the final, corrected code with these changes fully implemented:
|
|
268
|
-
|
|
269
|
-
Final Revised Code
|
|
270
|
-
SharedStateMC.js
|
|
167
|
+
The parent component attaches an instance of ParentMC to the DOM node and sets its value based on inputValue.
|
|
271
168
|
|
|
272
169
|
```javascript
|
|
273
|
-
import { MC } from '@twobirds/microcomponents';
|
|
274
|
-
|
|
275
|
-
class SharedStateMC extends MC {
|
|
276
|
-
constructor(target) {
|
|
277
|
-
super(target);
|
|
278
|
-
// Define an observable property for shared data with runtime type safety
|
|
279
|
-
this.sharedData = "Initial data from Parent";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export default SharedStateMC;
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
ParentComponent.js
|
|
288
170
|
|
|
289
|
-
|
|
290
|
-
import React, { useEffect } from 'react';
|
|
291
|
-
import SharedStateMC from './SharedStateMC';
|
|
171
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
292
172
|
import { MC } from '@twobirds/microcomponents';
|
|
293
173
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
174
|
+
const ParentComponent = () => {
|
|
175
|
+
const [inputValue, setInputValue] = useState("");
|
|
176
|
+
const parentRef = useRef(null);
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
const parentMC = new ParentMC(parentRef.current); // Pass the DOM node to ParentMC
|
|
180
|
+
MC.add(parentRef.current, 'parentMC', parentMC);
|
|
181
|
+
|
|
182
|
+
// Update value in ParentMC whenever inputValue changes
|
|
183
|
+
parentMC.value = inputValue;
|
|
184
|
+
}, [inputValue]);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<div ref={parentRef}>
|
|
188
|
+
<input
|
|
189
|
+
type="text"
|
|
190
|
+
value={inputValue}
|
|
191
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
192
|
+
placeholder="Type to send data to children"
|
|
193
|
+
/>
|
|
194
|
+
<ChildComponent />
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
317
198
|
```
|
|
318
199
|
|
|
200
|
+
Child Component
|
|
319
201
|
|
|
320
|
-
|
|
202
|
+
The child component attaches an instance of ChildMC, which updates its state whenever data is set.
|
|
321
203
|
|
|
322
204
|
```javascript
|
|
323
|
-
import React, { useEffect, useState } from 'react';
|
|
324
|
-
|
|
325
|
-
function ChildComponent() {
|
|
326
|
-
const [data, setData] = useState("");
|
|
327
|
-
|
|
328
|
-
useEffect(() => {
|
|
329
|
-
const parentElement = document.getElementById("parent-root");
|
|
330
|
-
const sharedStateMC = parentElement._mc.sharedState;
|
|
331
|
-
|
|
332
|
-
// Initial data load
|
|
333
|
-
setData(sharedStateMC.sharedData);
|
|
334
|
-
|
|
335
|
-
// Observing updates to sharedData property
|
|
336
|
-
sharedStateMC.sharedData.observe((newData) => {
|
|
337
|
-
setData(newData);
|
|
338
|
-
});
|
|
339
|
-
}, []);
|
|
340
|
-
|
|
341
|
-
return (
|
|
342
|
-
<div>
|
|
343
|
-
<h2>Child Component</h2>
|
|
344
|
-
<p>Data from Parent: {data}</p>
|
|
345
|
-
</div>
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
205
|
|
|
349
|
-
|
|
206
|
+
const ChildComponent = () => {
|
|
207
|
+
const [childData, setChildData] = useState("");
|
|
208
|
+
const childRef = useRef(null);
|
|
209
|
+
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
const childMC = new ChildMC(childRef.current, setChildData); // Pass DOM node and state setter to ChildMC
|
|
212
|
+
MC.add(childRef.current, 'targetMC', childMC);
|
|
213
|
+
}, []);
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div ref={childRef}>
|
|
217
|
+
<h3>Received Data:</h3>
|
|
218
|
+
<p>{childData}</p>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
};
|
|
350
222
|
```
|
|
351
223
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
Observables: The sharedData property in SharedStateMC is now noted to be observable and accessible as a public interface.
|
|
355
|
-
|
|
356
|
-
Microcomponent Attaching: Using MC.add clarifies the attachment of the SharedStateMC instance to parentElement.
|
|
357
|
-
|
|
358
|
-
Runtime Type Safety: The property remains observable, allowing callbacks on sharedData in ChildComponent.
|
|
359
|
-
|
|
360
|
-
This implementation accurately reflects the design principles and interfaces for twoBirds MCs, allowing cross-component data sharing while ensuring type safety and a clean observable setup.
|
|
224
|
+
***( ... I cannot tell about the react part, but the micro coomponent code is spot on. Like WLAN on the DOM :-) )***
|
|
361
225
|
|
|
362
226
|
|
|
363
227
|
**ChatGPT can produce errors. Check the information.**
|