@effect-rx/rx-react 0.39.1 → 0.40.1

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.
@@ -41,9 +41,54 @@ const HydrationBoundary = ({
41
41
  state
42
42
  }) => {
43
43
  const registry = React.useContext(_RegistryContext.RegistryContext);
44
- React.useEffect(() => {
45
- Hydration.hydrate(registry, state);
44
+ // This useMemo is for performance reasons only, everything inside it must
45
+ // be safe to run in every render and code here should be read as "in render".
46
+ //
47
+ // This code needs to happen during the render phase, because after initial
48
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
49
+ // during a transition, we want to hydrate as much as is safe in render so
50
+ // we can prerender as much as possible.
51
+ //
52
+ // For any Rx values that already exist in the registry, we want to hold back on
53
+ // hydrating until _after_ the render phase. The reason for this is that during
54
+ // transitions, we don't want the existing Rx values and subscribers to update to
55
+ // the new data on the current page, only _after_ the transition is committed.
56
+ // If the transition is aborted, we will have hydrated any _new_ Rx values, but
57
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
58
+ // updating the UI.
59
+ const hydrationQueue = React.useMemo(() => {
60
+ if (state) {
61
+ const dehydratedRxs = Array.from(state);
62
+ const nodes = registry.getNodes();
63
+ const newDehydratedRxs = [];
64
+ const existingDehydratedRxs = [];
65
+ for (const dehydratedRx of dehydratedRxs) {
66
+ const existingNode = nodes.get(dehydratedRx.key);
67
+ if (!existingNode) {
68
+ // This is a new Rx value, safe to hydrate immediately
69
+ newDehydratedRxs.push(dehydratedRx);
70
+ } else {
71
+ // This Rx value already exists, queue it for later hydration
72
+ // TODO: Add logic to check if hydration data is newer
73
+ existingDehydratedRxs.push(dehydratedRx);
74
+ }
75
+ }
76
+ if (newDehydratedRxs.length > 0) {
77
+ // It's actually fine to call this with state that already exists
78
+ // in the registry, or is older. hydrate() is idempotent.
79
+ Hydration.hydrate(registry, newDehydratedRxs);
80
+ }
81
+ if (existingDehydratedRxs.length > 0) {
82
+ return existingDehydratedRxs;
83
+ }
84
+ }
85
+ return undefined;
46
86
  }, [registry, state]);
87
+ React.useEffect(() => {
88
+ if (hydrationQueue) {
89
+ Hydration.hydrate(registry, hydrationQueue);
90
+ }
91
+ }, [registry, hydrationQueue]);
47
92
  return React.createElement(React.Fragment, {}, children);
48
93
  };
49
94
  exports.HydrationBoundary = HydrationBoundary;
@@ -1 +1 @@
1
- {"version":3,"file":"ReactHydration.js","names":["Object","defineProperty","exports","value","HydrationBoundary","Hydration","_interopRequireWildcard","require","React","_RegistryContext","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","getOwnPropertyDescriptor","children","state","registry","useContext","RegistryContext","useEffect","hydrate","createElement","Fragment"],"sources":["../../src/ReactHydration.ts"],"sourcesContent":[null],"mappings":";;AAAA;;;AAGA,YAAY;;AAAAA,MAAA,CAAAC,cAAA,CAAAC,OAAA;EAAAC,KAAA;AAAA;AAAAD,OAAA,CAAAE,iBAAA;AACZ,IAAAC,SAAA,gBAAAC,uBAAA,cAAAC,OAAA;AACA,IAAAC,KAAA,gBAAAF,uBAAA,cAAAC,OAAA;AACA,IAAAE,gBAAA,gBAAAF,OAAA;AAAsD,SAAAD,wBAAAI,CAAA,EAAAC,CAAA;EAAA,yBAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA;IAAAE,CAAA,OAAAF,OAAA;EAAA,QAAAN,uBAAA,YAAAA,CAAAI,CAAA,EAAAC,CAAA;IAAA,KAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA;IAAA,IAAAM,CAAA;MAAAC,CAAA;MAAAC,CAAA;QAAAC,SAAA;QAAAC,OAAA,EAAAV;MAAA;IAAA,aAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA;IAAA,IAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA;MAAA,IAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA;MAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA;IAAA;IAAA,WAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAhB,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAA0B,wBAAA,CAAAhB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA;IAAA,OAAAO,CAAA;EAAA,GAAAR,CAAA,EAAAC,CAAA;AAAA;AAEtD;;;;AAIO,MAAMP,iBAAiB,GAGzBA,CAAC;EAAEuB,QAAQ;EAAEC;AAAK,CAAE,KAAI;EAC3B,MAAMC,QAAQ,GAAGrB,KAAK,CAACsB,UAAU,CAACrB,gBAAA,CAAAsB,eAAe,CAAC;EAClDvB,KAAK,CAACwB,SAAS,CAAC,MAAK;IACnB3B,SAAS,CAAC4B,OAAO,CAACJ,QAAQ,EAAED,KAAK,CAAC;EACpC,CAAC,EAAE,CAACC,QAAQ,EAAED,KAAK,CAAC,CAAC;EACrB,OAAOpB,KAAK,CAAC0B,aAAa,CAAC1B,KAAK,CAAC2B,QAAQ,EAAE,EAAE,EAAER,QAAQ,CAAC;AAC1D,CAAC;AAAAzB,OAAA,CAAAE,iBAAA,GAAAA,iBAAA","ignoreList":[]}
1
+ {"version":3,"file":"ReactHydration.js","names":["Object","defineProperty","exports","value","HydrationBoundary","Hydration","_interopRequireWildcard","require","React","_RegistryContext","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","getOwnPropertyDescriptor","children","state","registry","useContext","RegistryContext","hydrationQueue","useMemo","dehydratedRxs","Array","from","nodes","getNodes","newDehydratedRxs","existingDehydratedRxs","dehydratedRx","existingNode","key","push","length","hydrate","undefined","useEffect","createElement","Fragment"],"sources":["../../src/ReactHydration.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -4,8 +4,13 @@ import * as React from "react";
4
4
  * @since 1.0.0
5
5
  * @category components
6
6
  */
7
- export declare const HydrationBoundary: React.FC<{
8
- state: Iterable<Hydration.DehydratedRx>;
7
+ export interface HydrationBoundaryProps {
8
+ state?: Iterable<Hydration.DehydratedRx>;
9
9
  children?: React.ReactNode;
10
- }>;
10
+ }
11
+ /**
12
+ * @since 1.0.0
13
+ * @category components
14
+ */
15
+ export declare const HydrationBoundary: React.FC<HydrationBoundaryProps>;
11
16
  //# sourceMappingURL=ReactHydration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReactHydration.d.ts","sourceRoot":"","sources":["../../src/ReactHydration.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,SAAS,MAAM,yBAAyB,CAAA;AACpD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC;IACvC,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IACvC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,CAMA,CAAA"}
1
+ {"version":3,"file":"ReactHydration.d.ts","sourceRoot":"","sources":["../../src/ReactHydration.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,SAAS,MAAM,yBAAyB,CAAA;AACpD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA8D9D,CAAA"}
@@ -15,9 +15,54 @@ export const HydrationBoundary = ({
15
15
  state
16
16
  }) => {
17
17
  const registry = React.useContext(RegistryContext);
18
- React.useEffect(() => {
19
- Hydration.hydrate(registry, state);
18
+ // This useMemo is for performance reasons only, everything inside it must
19
+ // be safe to run in every render and code here should be read as "in render".
20
+ //
21
+ // This code needs to happen during the render phase, because after initial
22
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
23
+ // during a transition, we want to hydrate as much as is safe in render so
24
+ // we can prerender as much as possible.
25
+ //
26
+ // For any Rx values that already exist in the registry, we want to hold back on
27
+ // hydrating until _after_ the render phase. The reason for this is that during
28
+ // transitions, we don't want the existing Rx values and subscribers to update to
29
+ // the new data on the current page, only _after_ the transition is committed.
30
+ // If the transition is aborted, we will have hydrated any _new_ Rx values, but
31
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
32
+ // updating the UI.
33
+ const hydrationQueue = React.useMemo(() => {
34
+ if (state) {
35
+ const dehydratedRxs = Array.from(state);
36
+ const nodes = registry.getNodes();
37
+ const newDehydratedRxs = [];
38
+ const existingDehydratedRxs = [];
39
+ for (const dehydratedRx of dehydratedRxs) {
40
+ const existingNode = nodes.get(dehydratedRx.key);
41
+ if (!existingNode) {
42
+ // This is a new Rx value, safe to hydrate immediately
43
+ newDehydratedRxs.push(dehydratedRx);
44
+ } else {
45
+ // This Rx value already exists, queue it for later hydration
46
+ // TODO: Add logic to check if hydration data is newer
47
+ existingDehydratedRxs.push(dehydratedRx);
48
+ }
49
+ }
50
+ if (newDehydratedRxs.length > 0) {
51
+ // It's actually fine to call this with state that already exists
52
+ // in the registry, or is older. hydrate() is idempotent.
53
+ Hydration.hydrate(registry, newDehydratedRxs);
54
+ }
55
+ if (existingDehydratedRxs.length > 0) {
56
+ return existingDehydratedRxs;
57
+ }
58
+ }
59
+ return undefined;
20
60
  }, [registry, state]);
61
+ React.useEffect(() => {
62
+ if (hydrationQueue) {
63
+ Hydration.hydrate(registry, hydrationQueue);
64
+ }
65
+ }, [registry, hydrationQueue]);
21
66
  return React.createElement(React.Fragment, {}, children);
22
67
  };
23
68
  //# sourceMappingURL=ReactHydration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReactHydration.js","names":["Hydration","React","RegistryContext","HydrationBoundary","children","state","registry","useContext","useEffect","hydrate","createElement","Fragment"],"sources":["../../src/ReactHydration.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,YAAY;;AACZ,OAAO,KAAKA,SAAS,MAAM,yBAAyB;AACpD,OAAO,KAAKC,KAAK,MAAM,OAAO;AAC9B,SAASC,eAAe,QAAQ,sBAAsB;AAEtD;;;;AAIA,OAAO,MAAMC,iBAAiB,GAGzBA,CAAC;EAAEC,QAAQ;EAAEC;AAAK,CAAE,KAAI;EAC3B,MAAMC,QAAQ,GAAGL,KAAK,CAACM,UAAU,CAACL,eAAe,CAAC;EAClDD,KAAK,CAACO,SAAS,CAAC,MAAK;IACnBR,SAAS,CAACS,OAAO,CAACH,QAAQ,EAAED,KAAK,CAAC;EACpC,CAAC,EAAE,CAACC,QAAQ,EAAED,KAAK,CAAC,CAAC;EACrB,OAAOJ,KAAK,CAACS,aAAa,CAACT,KAAK,CAACU,QAAQ,EAAE,EAAE,EAAEP,QAAQ,CAAC;AAC1D,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"ReactHydration.js","names":["Hydration","React","RegistryContext","HydrationBoundary","children","state","registry","useContext","hydrationQueue","useMemo","dehydratedRxs","Array","from","nodes","getNodes","newDehydratedRxs","existingDehydratedRxs","dehydratedRx","existingNode","get","key","push","length","hydrate","undefined","useEffect","createElement","Fragment"],"sources":["../../src/ReactHydration.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,YAAY;;AACZ,OAAO,KAAKA,SAAS,MAAM,yBAAyB;AACpD,OAAO,KAAKC,KAAK,MAAM,OAAO;AAC9B,SAASC,eAAe,QAAQ,sBAAsB;AAWtD;;;;AAIA,OAAO,MAAMC,iBAAiB,GAAqCA,CAAC;EAClEC,QAAQ;EACRC;AAAK,CACN,KAAI;EACH,MAAMC,QAAQ,GAAGL,KAAK,CAACM,UAAU,CAACL,eAAe,CAAC;EAElD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAMM,cAAc,GAA8CP,KAAK,CAACQ,OAAO,CAAC,MAAK;IACnF,IAAIJ,KAAK,EAAE;MACT,MAAMK,aAAa,GAAGC,KAAK,CAACC,IAAI,CAACP,KAAK,CAAC;MACvC,MAAMQ,KAAK,GAAGP,QAAQ,CAACQ,QAAQ,EAAE;MAEjC,MAAMC,gBAAgB,GAAkC,EAAE;MAC1D,MAAMC,qBAAqB,GAAkC,EAAE;MAE/D,KAAK,MAAMC,YAAY,IAAIP,aAAa,EAAE;QACxC,MAAMQ,YAAY,GAAGL,KAAK,CAACM,GAAG,CAACF,YAAY,CAACG,GAAG,CAAC;QAEhD,IAAI,CAACF,YAAY,EAAE;UACjB;UACAH,gBAAgB,CAACM,IAAI,CAACJ,YAAY,CAAC;QACrC,CAAC,MAAM;UACL;UACA;UACAD,qBAAqB,CAACK,IAAI,CAACJ,YAAY,CAAC;QAC1C;MACF;MAEA,IAAIF,gBAAgB,CAACO,MAAM,GAAG,CAAC,EAAE;QAC/B;QACA;QACAtB,SAAS,CAACuB,OAAO,CAACjB,QAAQ,EAAES,gBAAgB,CAAC;MAC/C;MAEA,IAAIC,qBAAqB,CAACM,MAAM,GAAG,CAAC,EAAE;QACpC,OAAON,qBAAqB;MAC9B;IACF;IACA,OAAOQ,SAAS;EAClB,CAAC,EAAE,CAAClB,QAAQ,EAAED,KAAK,CAAC,CAAC;EAErBJ,KAAK,CAACwB,SAAS,CAAC,MAAK;IACnB,IAAIjB,cAAc,EAAE;MAClBR,SAAS,CAACuB,OAAO,CAACjB,QAAQ,EAAEE,cAAc,CAAC;IAC7C;EACF,CAAC,EAAE,CAACF,QAAQ,EAAEE,cAAc,CAAC,CAAC;EAE9B,OAAOP,KAAK,CAACyB,aAAa,CAACzB,KAAK,CAAC0B,QAAQ,EAAE,EAAE,EAAEvB,QAAQ,CAAC;AAC1D,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-rx/rx-react",
3
- "version": "0.39.1",
3
+ "version": "0.40.1",
4
4
  "description": "Reactive toolkit for Effect",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,10 +11,10 @@
11
11
  "author": "Effect contributors",
12
12
  "homepage": "https://github.com/effect-ts/rx",
13
13
  "dependencies": {
14
- "@effect-rx/rx": "^0.45.0"
14
+ "@effect-rx/rx": "^0.46.1"
15
15
  },
16
16
  "peerDependencies": {
17
- "effect": "^3.16",
17
+ "effect": "^3.17",
18
18
  "react": ">=18 <20",
19
19
  "scheduler": "*"
20
20
  },
@@ -10,13 +10,75 @@ import { RegistryContext } from "./RegistryContext.js"
10
10
  * @since 1.0.0
11
11
  * @category components
12
12
  */
13
- export const HydrationBoundary: React.FC<{
14
- state: Iterable<Hydration.DehydratedRx>
13
+ export interface HydrationBoundaryProps {
14
+ state?: Iterable<Hydration.DehydratedRx>
15
15
  children?: React.ReactNode
16
- }> = ({ children, state }) => {
16
+ }
17
+
18
+ /**
19
+ * @since 1.0.0
20
+ * @category components
21
+ */
22
+ export const HydrationBoundary: React.FC<HydrationBoundaryProps> = ({
23
+ children,
24
+ state
25
+ }) => {
17
26
  const registry = React.useContext(RegistryContext)
18
- React.useEffect(() => {
19
- Hydration.hydrate(registry, state)
27
+
28
+ // This useMemo is for performance reasons only, everything inside it must
29
+ // be safe to run in every render and code here should be read as "in render".
30
+ //
31
+ // This code needs to happen during the render phase, because after initial
32
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
33
+ // during a transition, we want to hydrate as much as is safe in render so
34
+ // we can prerender as much as possible.
35
+ //
36
+ // For any Rx values that already exist in the registry, we want to hold back on
37
+ // hydrating until _after_ the render phase. The reason for this is that during
38
+ // transitions, we don't want the existing Rx values and subscribers to update to
39
+ // the new data on the current page, only _after_ the transition is committed.
40
+ // If the transition is aborted, we will have hydrated any _new_ Rx values, but
41
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
42
+ // updating the UI.
43
+ const hydrationQueue: Array<Hydration.DehydratedRx> | undefined = React.useMemo(() => {
44
+ if (state) {
45
+ const dehydratedRxs = Array.from(state)
46
+ const nodes = registry.getNodes()
47
+
48
+ const newDehydratedRxs: Array<Hydration.DehydratedRx> = []
49
+ const existingDehydratedRxs: Array<Hydration.DehydratedRx> = []
50
+
51
+ for (const dehydratedRx of dehydratedRxs) {
52
+ const existingNode = nodes.get(dehydratedRx.key)
53
+
54
+ if (!existingNode) {
55
+ // This is a new Rx value, safe to hydrate immediately
56
+ newDehydratedRxs.push(dehydratedRx)
57
+ } else {
58
+ // This Rx value already exists, queue it for later hydration
59
+ // TODO: Add logic to check if hydration data is newer
60
+ existingDehydratedRxs.push(dehydratedRx)
61
+ }
62
+ }
63
+
64
+ if (newDehydratedRxs.length > 0) {
65
+ // It's actually fine to call this with state that already exists
66
+ // in the registry, or is older. hydrate() is idempotent.
67
+ Hydration.hydrate(registry, newDehydratedRxs)
68
+ }
69
+
70
+ if (existingDehydratedRxs.length > 0) {
71
+ return existingDehydratedRxs
72
+ }
73
+ }
74
+ return undefined
20
75
  }, [registry, state])
76
+
77
+ React.useEffect(() => {
78
+ if (hydrationQueue) {
79
+ Hydration.hydrate(registry, hydrationQueue)
80
+ }
81
+ }, [registry, hydrationQueue])
82
+
21
83
  return React.createElement(React.Fragment, {}, children)
22
84
  }