@creact-labs/creact 0.2.7 → 0.2.9

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.
@@ -26,7 +26,7 @@ function shallowEqual(a, b) {
26
26
  }
27
27
  // Registry of all instance nodes by ID
28
28
  const nodeRegistry = new Map();
29
- // Track which fiber owns each nodeId (to detect duplicate siblings)
29
+ // Track which fiber path owns each nodeId (to detect duplicate siblings)
30
30
  const nodeOwnership = new Map();
31
31
  // Output hydration map - populated before render to restore outputs from previous run
32
32
  const outputHydrationMap = new Map();
@@ -99,16 +99,17 @@ export function useInstance(construct, props) {
99
99
  }
100
100
  const constructType = construct.name || 'Unknown';
101
101
  // Check for duplicate siblings (requires keys)
102
- // Allow same fiber to re-register (reactive re-render), but not different fibers
103
- const existingOwner = nodeOwnership.get(nodeId);
104
- if (existingOwner && existingOwner !== fiber) {
102
+ // Allow same fiber path to re-register (reactive re-render), but not different paths
103
+ const existingOwnerPath = nodeOwnership.get(nodeId);
104
+ const currentFiberPath = fiber.path.join('.');
105
+ if (existingOwnerPath && existingOwnerPath !== currentFiberPath) {
105
106
  throw new Error(`Multiple instances of ${constructType} at the same level require unique keys.\n` +
106
107
  `Add a key prop to differentiate them:\n\n` +
107
108
  ` {items.map((item) => (\n` +
108
109
  ` <MyResource key={item.id} ... />\n` +
109
110
  ` ))}`);
110
111
  }
111
- nodeOwnership.set(nodeId, fiber);
112
+ nodeOwnership.set(nodeId, currentFiberPath);
112
113
  // Create or get existing node
113
114
  let node = nodeRegistry.get(nodeId);
114
115
  if (!node) {
@@ -121,9 +122,23 @@ export function useInstance(construct, props) {
121
122
  outputSignals: new Map(),
122
123
  children: [],
123
124
  setOutputs(outputs) {
124
- // Clear ownership before batch triggers re-renders
125
- // When signals update, components re-execute and create new fibers
126
- // that need to claim the same nodeIds
125
+ // First check if any values actually changed
126
+ let hasChanges = false;
127
+ for (const [key, value] of Object.entries(outputs)) {
128
+ if (!this.outputSignals.has(key)) {
129
+ hasChanges = true;
130
+ break;
131
+ }
132
+ const [read] = this.outputSignals.get(key);
133
+ if (!shallowEqual(read(), value)) {
134
+ hasChanges = true;
135
+ break;
136
+ }
137
+ }
138
+ // Early exit if nothing changed - no re-render needed
139
+ if (!hasChanges)
140
+ return;
141
+ // Only clear ownership and batch if there are actual changes
127
142
  nodeOwnership.clear();
128
143
  batch(() => {
129
144
  for (const [key, value] of Object.entries(outputs)) {
@@ -132,7 +147,6 @@ export function useInstance(construct, props) {
132
147
  }
133
148
  else {
134
149
  const [read, write] = this.outputSignals.get(key);
135
- // Only update if value actually changed
136
150
  if (!shallowEqual(read(), value)) {
137
151
  write(value);
138
152
  }
@@ -214,15 +228,28 @@ export function fillInstanceOutputs(nodeId, outputs) {
214
228
  const node = nodeRegistry.get(nodeId);
215
229
  if (!node)
216
230
  return;
217
- // Clear ownership before batch triggers re-renders
218
- // When signals update, components re-execute and create new fibers
219
- // that need to claim the same nodeIds
231
+ // First check if any values actually changed
232
+ let hasChanges = false;
233
+ for (const [key, value] of Object.entries(outputs)) {
234
+ if (!node.outputSignals.has(key)) {
235
+ hasChanges = true;
236
+ break;
237
+ }
238
+ const [read] = node.outputSignals.get(key);
239
+ if (!shallowEqual(read(), value)) {
240
+ hasChanges = true;
241
+ break;
242
+ }
243
+ }
244
+ // Early exit if nothing changed - no re-render needed
245
+ if (!hasChanges)
246
+ return;
247
+ // Only clear ownership and batch if there are actual changes
220
248
  nodeOwnership.clear();
221
249
  batch(() => {
222
250
  for (const [key, value] of Object.entries(outputs)) {
223
251
  if (node.outputSignals.has(key)) {
224
252
  const [read, write] = node.outputSignals.get(key);
225
- // Only update if value actually changed
226
253
  if (!shallowEqual(read(), value)) {
227
254
  write(value);
228
255
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@creact-labs/creact",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
5
  "description": "Declarative universal reactive runtime",
6
6
  "main": "dist/index.js",