@effect-rx/rx-react 0.39.1 → 0.40.0

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.
@@ -38,12 +38,60 @@ function _interopRequireWildcard(e, t) {
38
38
  */
39
39
  const HydrationBoundary = ({
40
40
  children,
41
+ options = {},
41
42
  state
42
43
  }) => {
43
44
  const registry = React.useContext(_RegistryContext.RegistryContext);
44
- React.useEffect(() => {
45
- Hydration.hydrate(registry, state);
45
+ const optionsRef = React.useRef(options);
46
+ optionsRef.current = options;
47
+ // This useMemo is for performance reasons only, everything inside it must
48
+ // be safe to run in every render and code here should be read as "in render".
49
+ //
50
+ // This code needs to happen during the render phase, because after initial
51
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
52
+ // during a transition, we want to hydrate as much as is safe in render so
53
+ // we can prerender as much as possible.
54
+ //
55
+ // For any Rx values that already exist in the registry, we want to hold back on
56
+ // hydrating until _after_ the render phase. The reason for this is that during
57
+ // transitions, we don't want the existing Rx values and subscribers to update to
58
+ // the new data on the current page, only _after_ the transition is committed.
59
+ // If the transition is aborted, we will have hydrated any _new_ Rx values, but
60
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
61
+ // updating the UI.
62
+ const hydrationQueue = React.useMemo(() => {
63
+ if (state) {
64
+ const dehydratedRxs = Array.from(state);
65
+ const nodes = registry.getNodes();
66
+ const newDehydratedRxs = [];
67
+ const existingDehydratedRxs = [];
68
+ for (const dehydratedRx of dehydratedRxs) {
69
+ const existingNode = nodes.get(dehydratedRx.key);
70
+ if (!existingNode) {
71
+ // This is a new Rx value, safe to hydrate immediately
72
+ newDehydratedRxs.push(dehydratedRx);
73
+ } else {
74
+ // This Rx value already exists, queue it for later hydration
75
+ // TODO: Add logic to check if hydration data is newer
76
+ existingDehydratedRxs.push(dehydratedRx);
77
+ }
78
+ }
79
+ if (newDehydratedRxs.length > 0) {
80
+ // It's actually fine to call this with state that already exists
81
+ // in the registry, or is older. hydrate() is idempotent.
82
+ Hydration.hydrate(registry, newDehydratedRxs, optionsRef.current);
83
+ }
84
+ if (existingDehydratedRxs.length > 0) {
85
+ return existingDehydratedRxs;
86
+ }
87
+ }
88
+ return undefined;
46
89
  }, [registry, state]);
90
+ React.useEffect(() => {
91
+ if (hydrationQueue) {
92
+ Hydration.hydrate(registry, hydrationQueue, optionsRef.current);
93
+ }
94
+ }, [registry, hydrationQueue]);
47
95
  return React.createElement(React.Fragment, {}, children);
48
96
  };
49
97
  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","options","state","registry","useContext","RegistryContext","optionsRef","useRef","current","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,16 @@ 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
+ options?: {
10
+ readonly shouldHydrateRx?: ((dehydratedRx: Hydration.DehydratedRx) => boolean) | undefined;
11
+ };
9
12
  children?: React.ReactNode;
10
- }>;
13
+ }
14
+ /**
15
+ * @since 1.0.0
16
+ * @category components
17
+ */
18
+ export declare const HydrationBoundary: React.FC<HydrationBoundaryProps>;
11
19
  //# 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,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,YAAY,EAAE,SAAS,CAAC,YAAY,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;KAC3F,CAAA;IACD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAkE9D,CAAA"}
@@ -12,12 +12,60 @@ import { RegistryContext } from "./RegistryContext.js";
12
12
  */
13
13
  export const HydrationBoundary = ({
14
14
  children,
15
+ options = {},
15
16
  state
16
17
  }) => {
17
18
  const registry = React.useContext(RegistryContext);
18
- React.useEffect(() => {
19
- Hydration.hydrate(registry, state);
19
+ const optionsRef = React.useRef(options);
20
+ optionsRef.current = options;
21
+ // This useMemo is for performance reasons only, everything inside it must
22
+ // be safe to run in every render and code here should be read as "in render".
23
+ //
24
+ // This code needs to happen during the render phase, because after initial
25
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
26
+ // during a transition, we want to hydrate as much as is safe in render so
27
+ // we can prerender as much as possible.
28
+ //
29
+ // For any Rx values that already exist in the registry, we want to hold back on
30
+ // hydrating until _after_ the render phase. The reason for this is that during
31
+ // transitions, we don't want the existing Rx values and subscribers to update to
32
+ // the new data on the current page, only _after_ the transition is committed.
33
+ // If the transition is aborted, we will have hydrated any _new_ Rx values, but
34
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
35
+ // updating the UI.
36
+ const hydrationQueue = React.useMemo(() => {
37
+ if (state) {
38
+ const dehydratedRxs = Array.from(state);
39
+ const nodes = registry.getNodes();
40
+ const newDehydratedRxs = [];
41
+ const existingDehydratedRxs = [];
42
+ for (const dehydratedRx of dehydratedRxs) {
43
+ const existingNode = nodes.get(dehydratedRx.key);
44
+ if (!existingNode) {
45
+ // This is a new Rx value, safe to hydrate immediately
46
+ newDehydratedRxs.push(dehydratedRx);
47
+ } else {
48
+ // This Rx value already exists, queue it for later hydration
49
+ // TODO: Add logic to check if hydration data is newer
50
+ existingDehydratedRxs.push(dehydratedRx);
51
+ }
52
+ }
53
+ if (newDehydratedRxs.length > 0) {
54
+ // It's actually fine to call this with state that already exists
55
+ // in the registry, or is older. hydrate() is idempotent.
56
+ Hydration.hydrate(registry, newDehydratedRxs, optionsRef.current);
57
+ }
58
+ if (existingDehydratedRxs.length > 0) {
59
+ return existingDehydratedRxs;
60
+ }
61
+ }
62
+ return undefined;
20
63
  }, [registry, state]);
64
+ React.useEffect(() => {
65
+ if (hydrationQueue) {
66
+ Hydration.hydrate(registry, hydrationQueue, optionsRef.current);
67
+ }
68
+ }, [registry, hydrationQueue]);
21
69
  return React.createElement(React.Fragment, {}, children);
22
70
  };
23
71
  //# 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","options","state","registry","useContext","optionsRef","useRef","current","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;AActD;;;;AAIA,OAAO,MAAMC,iBAAiB,GAAqCA,CAAC;EAClEC,QAAQ;EACRC,OAAO,GAAG,EAAE;EACZC;AAAK,CACN,KAAI;EACH,MAAMC,QAAQ,GAAGN,KAAK,CAACO,UAAU,CAACN,eAAe,CAAC;EAElD,MAAMO,UAAU,GAAGR,KAAK,CAACS,MAAM,CAACL,OAAO,CAAC;EACxCI,UAAU,CAACE,OAAO,GAAGN,OAAO;EAE5B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAMO,cAAc,GAA8CX,KAAK,CAACY,OAAO,CAAC,MAAK;IACnF,IAAIP,KAAK,EAAE;MACT,MAAMQ,aAAa,GAAGC,KAAK,CAACC,IAAI,CAACV,KAAK,CAAC;MACvC,MAAMW,KAAK,GAAGV,QAAQ,CAACW,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;QACA1B,SAAS,CAAC2B,OAAO,CAACpB,QAAQ,EAAEY,gBAAgB,EAAEV,UAAU,CAACE,OAAO,CAAC;MACnE;MAEA,IAAIS,qBAAqB,CAACM,MAAM,GAAG,CAAC,EAAE;QACpC,OAAON,qBAAqB;MAC9B;IACF;IACA,OAAOQ,SAAS;EAClB,CAAC,EAAE,CAACrB,QAAQ,EAAED,KAAK,CAAC,CAAC;EAErBL,KAAK,CAAC4B,SAAS,CAAC,MAAK;IACnB,IAAIjB,cAAc,EAAE;MAClBZ,SAAS,CAAC2B,OAAO,CAACpB,QAAQ,EAAEK,cAAc,EAAEH,UAAU,CAACE,OAAO,CAAC;IACjE;EACF,CAAC,EAAE,CAACJ,QAAQ,EAAEK,cAAc,CAAC,CAAC;EAE9B,OAAOX,KAAK,CAAC6B,aAAa,CAAC7B,KAAK,CAAC8B,QAAQ,EAAE,EAAE,EAAE3B,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.0",
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.0"
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,82 @@ 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
+ options?: {
16
+ readonly shouldHydrateRx?: ((dehydratedRx: Hydration.DehydratedRx) => boolean) | undefined
17
+ }
15
18
  children?: React.ReactNode
16
- }> = ({ children, state }) => {
19
+ }
20
+
21
+ /**
22
+ * @since 1.0.0
23
+ * @category components
24
+ */
25
+ export const HydrationBoundary: React.FC<HydrationBoundaryProps> = ({
26
+ children,
27
+ options = {},
28
+ state
29
+ }) => {
17
30
  const registry = React.useContext(RegistryContext)
18
- React.useEffect(() => {
19
- Hydration.hydrate(registry, state)
31
+
32
+ const optionsRef = React.useRef(options)
33
+ optionsRef.current = options
34
+
35
+ // This useMemo is for performance reasons only, everything inside it must
36
+ // be safe to run in every render and code here should be read as "in render".
37
+ //
38
+ // This code needs to happen during the render phase, because after initial
39
+ // SSR, hydration needs to happen _before_ children render. Also, if hydrating
40
+ // during a transition, we want to hydrate as much as is safe in render so
41
+ // we can prerender as much as possible.
42
+ //
43
+ // For any Rx values that already exist in the registry, we want to hold back on
44
+ // hydrating until _after_ the render phase. The reason for this is that during
45
+ // transitions, we don't want the existing Rx values and subscribers to update to
46
+ // the new data on the current page, only _after_ the transition is committed.
47
+ // If the transition is aborted, we will have hydrated any _new_ Rx values, but
48
+ // we throw away the fresh data for any existing ones to avoid unexpectedly
49
+ // updating the UI.
50
+ const hydrationQueue: Array<Hydration.DehydratedRx> | undefined = React.useMemo(() => {
51
+ if (state) {
52
+ const dehydratedRxs = Array.from(state)
53
+ const nodes = registry.getNodes()
54
+
55
+ const newDehydratedRxs: Array<Hydration.DehydratedRx> = []
56
+ const existingDehydratedRxs: Array<Hydration.DehydratedRx> = []
57
+
58
+ for (const dehydratedRx of dehydratedRxs) {
59
+ const existingNode = nodes.get(dehydratedRx.key)
60
+
61
+ if (!existingNode) {
62
+ // This is a new Rx value, safe to hydrate immediately
63
+ newDehydratedRxs.push(dehydratedRx)
64
+ } else {
65
+ // This Rx value already exists, queue it for later hydration
66
+ // TODO: Add logic to check if hydration data is newer
67
+ existingDehydratedRxs.push(dehydratedRx)
68
+ }
69
+ }
70
+
71
+ if (newDehydratedRxs.length > 0) {
72
+ // It's actually fine to call this with state that already exists
73
+ // in the registry, or is older. hydrate() is idempotent.
74
+ Hydration.hydrate(registry, newDehydratedRxs, optionsRef.current)
75
+ }
76
+
77
+ if (existingDehydratedRxs.length > 0) {
78
+ return existingDehydratedRxs
79
+ }
80
+ }
81
+ return undefined
20
82
  }, [registry, state])
83
+
84
+ React.useEffect(() => {
85
+ if (hydrationQueue) {
86
+ Hydration.hydrate(registry, hydrationQueue, optionsRef.current)
87
+ }
88
+ }, [registry, hydrationQueue])
89
+
21
90
  return React.createElement(React.Fragment, {}, children)
22
91
  }