what-react 0.1.2 → 0.6.2

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 (3) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -4
  3. package/src/index.js +97 -15
package/README.md CHANGED
@@ -123,7 +123,7 @@ reactCompat({
123
123
 
124
124
  - [React Compat Showcase](https://react.whatfw.com)
125
125
  - [Documentation](https://whatfw.com)
126
- - [GitHub](https://github.com/CelsianJs/whatfw)
126
+ - [GitHub](https://github.com/CelsianJs/what-framework)
127
127
  - [Benchmarks](https://benchmarks.whatfw.com)
128
128
 
129
129
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-react",
3
- "version": "0.1.2",
3
+ "version": "0.6.2",
4
4
  "description": "React compatibility layer for What Framework — use React packages with signals under the hood",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -23,16 +23,16 @@
23
23
  "compatibility"
24
24
  ],
25
25
  "peerDependencies": {
26
- "what-core": "^0.5.3"
26
+ "what-core": "^0.6.2"
27
27
  },
28
28
  "author": "ZVN DEV (https://zvndev.com)",
29
29
  "license": "MIT",
30
30
  "repository": {
31
31
  "type": "git",
32
- "url": "https://github.com/CelsianJs/whatfw"
32
+ "url": "https://github.com/CelsianJs/what-framework"
33
33
  },
34
34
  "bugs": {
35
- "url": "https://github.com/CelsianJs/whatfw/issues"
35
+ "url": "https://github.com/CelsianJs/what-framework/issues"
36
36
  },
37
37
  "homepage": "https://react.whatfw.com"
38
38
  }
package/src/index.js CHANGED
@@ -22,11 +22,6 @@ import {
22
22
  lazy as whatLazy,
23
23
  Suspense as WhatSuspense,
24
24
  ErrorBoundary as WhatErrorBoundary,
25
- useState as whatUseState,
26
- useEffect as whatUseEffect,
27
- useMemo as whatUseMemo,
28
- useCallback as whatUseCallback,
29
- useRef as whatUseRef,
30
25
  useContext as whatUseContext,
31
26
  useReducer as whatUseReducer,
32
27
  createContext as whatCreateContext,
@@ -34,6 +29,16 @@ import {
34
29
  onCleanup,
35
30
  } from 'what-core';
36
31
 
32
+ // React-style hooks are not exported from what-core's main entry (by design).
33
+ // Import them from the hooks subpath for react-compat use only.
34
+ import {
35
+ useState as whatUseState,
36
+ useEffect as whatUseEffect,
37
+ useMemo as whatUseMemo,
38
+ useCallback as whatUseCallback,
39
+ useRef as whatUseRef,
40
+ } from 'what-core/hooks';
41
+
37
42
  // ---- Re-export What's hooks with React-compatible names ----
38
43
 
39
44
  export const useState = whatUseState;
@@ -355,17 +360,68 @@ export function isValidElement(object) {
355
360
  }
356
361
 
357
362
  // ---- useLayoutEffect ----
363
+ // Must run synchronously after DOM mutations but before paint.
364
+ // We use queueMicrotask for layout-level timing (runs before next rAF).
358
365
 
359
366
  export function useLayoutEffect(fn, deps) {
360
- return whatUseEffect(fn, deps);
367
+ const hookRef = whatUseRef({ deps: undefined, cleanup: null });
368
+
369
+ const hook = hookRef.current;
370
+
371
+ if (_depsChanged(hook.deps, deps)) {
372
+ // Run synchronously via microtask — before next paint but after DOM mutations
373
+ queueMicrotask(() => {
374
+ if (hook.cleanup) {
375
+ try { hook.cleanup(); } catch (e) { /* cleanup error */ }
376
+ hook.cleanup = null;
377
+ }
378
+ const result = fn();
379
+ if (typeof result === 'function') {
380
+ hook.cleanup = result;
381
+ }
382
+ });
383
+ hook.deps = deps;
384
+ }
385
+
386
+ // Register cleanup on unmount
387
+ onCleanup(() => {
388
+ if (hook.cleanup) {
389
+ try { hook.cleanup(); } catch (e) { /* cleanup error */ }
390
+ hook.cleanup = null;
391
+ }
392
+ });
361
393
  }
362
394
 
363
395
  // ---- useInsertionEffect ----
364
396
  // React 18 hook for CSS-in-JS libraries. Runs synchronously before layout effects.
365
- // We map it to useEffect since What doesn't have multi-phase commit.
397
+ // We run it immediately (synchronously) during render to ensure it runs before
398
+ // useLayoutEffect's microtask and useEffect's async scheduling.
366
399
 
367
400
  export function useInsertionEffect(fn, deps) {
368
- return whatUseEffect(fn, deps);
401
+ const hookRef = whatUseRef({ deps: undefined, cleanup: null });
402
+
403
+ const hook = hookRef.current;
404
+
405
+ if (_depsChanged(hook.deps, deps)) {
406
+ // Run synchronously — before layout effects
407
+ if (hook.cleanup) {
408
+ try { hook.cleanup(); } catch (e) { /* cleanup error */ }
409
+ hook.cleanup = null;
410
+ }
411
+ const result = fn();
412
+ if (typeof result === 'function') {
413
+ hook.cleanup = result;
414
+ }
415
+ hook.deps = deps;
416
+ }
417
+
418
+ // Register cleanup on unmount
419
+ onCleanup(() => {
420
+ if (hook.cleanup) {
421
+ try { hook.cleanup(); } catch (e) { /* cleanup error */ }
422
+ hook.cleanup = null;
423
+ }
424
+ });
369
425
  }
370
426
 
371
427
  // ---- useImperativeHandle ----
@@ -398,24 +454,40 @@ export function useId() {
398
454
  export function useDebugValue() {}
399
455
 
400
456
  // ---- useSyncExternalStore ----
457
+ // Uses a signal internally so that consumers get reactive updates.
458
+ // The signal is initialized with getSnapshot(), and updated via the store's
459
+ // subscribe callback. The returned signal function integrates with What's
460
+ // fine-grained reactivity — reading it inside an effect auto-tracks the dependency.
401
461
 
402
462
  export function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
403
- const [value, setValue] = whatUseState(() => getSnapshot());
404
- const snapshotRef = whatUseRef(getSnapshot());
463
+ // Create a signal initialized with the current snapshot
464
+ const storeSignal = whatUseRef(null);
465
+ if (storeSignal.current === null) {
466
+ storeSignal.current = signal(getSnapshot());
467
+ }
405
468
 
469
+ const sig = storeSignal.current;
470
+
471
+ // Subscribe to the store, updating the signal when the store changes.
472
+ // onCleanup handles unsubscription on unmount.
406
473
  whatUseEffect(() => {
407
474
  const handleChange = () => {
408
475
  const next = getSnapshot();
409
- if (!Object.is(snapshotRef.current, next)) {
410
- snapshotRef.current = next;
411
- setValue(next);
476
+ const prev = sig.peek();
477
+ if (!Object.is(prev, next)) {
478
+ sig.set(next);
412
479
  }
413
480
  };
481
+ // Sync in case store changed between render and effect
414
482
  handleChange();
415
- return subscribe(handleChange);
483
+ const unsubscribe = subscribe(handleChange);
484
+ return unsubscribe;
416
485
  }, [subscribe, getSnapshot]);
417
486
 
418
- return value;
487
+ // Return the signal function itself. In the run-once model, returning sig()
488
+ // would capture a snapshot that never updates. Returning the signal function
489
+ // lets the fine-grained runtime track it reactively when used in JSX.
490
+ return sig;
419
491
  }
420
492
 
421
493
  // ---- useTransition ----
@@ -519,6 +591,16 @@ PureComponent.prototype.shouldComponentUpdate = function(nextProps, nextState) {
519
591
 
520
592
  // ---- Internal helpers ----
521
593
 
594
+ function _depsChanged(oldDeps, newDeps) {
595
+ if (oldDeps === undefined) return true;
596
+ if (!oldDeps || !newDeps) return true;
597
+ if (oldDeps.length !== newDeps.length) return true;
598
+ for (let i = 0; i < oldDeps.length; i++) {
599
+ if (!Object.is(oldDeps[i], newDeps[i])) return true;
600
+ }
601
+ return false;
602
+ }
603
+
522
604
  function shallowEqual(a, b) {
523
605
  if (Object.is(a, b)) return true;
524
606
  if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;