react-snapshot-bridge 1.1.1 → 1.2.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.
package/README.md CHANGED
@@ -150,6 +150,7 @@ function ChatList({ messages }: { messages: string[] }) {
150
150
  | `capture` | `() => T` | yes | Called synchronously **before** DOM mutation. Return value is forwarded to `apply`. |
151
151
  | `apply` | `(snapshot: T) => void` | yes | Called synchronously **after** DOM mutation, before paint. Receives the value from `capture`. |
152
152
  | `enabled` | `boolean` | no | Defaults to `true`. When `false`, both `capture` and `apply` are skipped for that commit. Missed updates are not replayed. |
153
+ | `deps` | `readonly unknown[]` | no | Optional dependency array, compared shallowly (`Object.is`) against the previous commit. When provided, both callbacks run only on commits where at least one entry changed. When omitted, they run on every update. |
153
154
  | `children` | `React.ReactNode` | no | Optional. When provided, rendered as-is (wrapper pattern). When omitted, renders nothing. |
154
155
 
155
156
  The generic `T` is inferred from `capture`'s return type — you usually don't need to specify it.
@@ -224,6 +225,7 @@ The captured value is forwarded to `componentDidUpdate`, which calls `apply`. Fr
224
225
  - **Updates run before DOM mutation for that commit.** After mount, whenever the bridge commits an update, `capture` runs in the **before mutation** phase: before the DOM reflects that commit's output and before the browser paints that update. That is the timing slot this library occupies (see [How it works](#how-it-works)).
225
226
  - **Requires a re-render in the right place.** If a `React.memo` (or `shouldComponentUpdate`) ancestor short-circuits the render, or the bridge is placed as a sibling of a different component that updates on its own, neither callback will fire. See [Where to place it](#where-to-place-it).
226
227
  - **`enabled={false}` does not buffer updates.** Updates that occur while disabled are silently dropped. There is no "catch up" call when you re-enable the bridge — the next regular update is what triggers `capture`/`apply` again.
228
+ - **`deps={[]}` differs from `useEffect`.** Because an empty array never changes and this lifecycle never runs on mount, `deps={[]}` means "never run after mount" — not "run once on mount" as it would with `useEffect`. Use a populated `deps` array (e.g. `[messages.length]`) to gate by meaningful changes.
227
229
  - **Concurrent rendering safe.** React 18+ concurrent features may discard render results, but the commit phase itself is synchronous, so `capture`/`apply` always run as a paired set.
228
230
  - **SSR safe.** Lifecycle methods don't run during `renderToString` / `renderToPipeableStream`, so the bridge is a no-op on the server.
229
231
  - **React 19.** Class components and `getSnapshotBeforeUpdate` are not deprecated in React 19. This library remains compatible.
package/dist/index.cjs CHANGED
@@ -2,17 +2,29 @@
2
2
 
3
3
  var react = require('react');
4
4
 
5
+ // src/SnapshotBeforeUpdate.tsx
6
+
7
+ // src/_utils/checkIsDepsEqual.ts
8
+ function checkIsDepsEqual(prev, next) {
9
+ if (prev === void 0 || next === void 0) return false;
10
+ if (prev.length !== next.length) return false;
11
+ for (let i = 0; i < prev.length; i++) {
12
+ if (!Object.is(prev[i], next[i])) return false;
13
+ }
14
+ return true;
15
+ }
16
+ var checkIsDepsEqual_default = checkIsDepsEqual;
17
+
5
18
  // src/SnapshotBeforeUpdate.tsx
6
19
  var SKIP = /* @__PURE__ */ Symbol("react-snapshot-bridge-skip");
7
20
  var SnapshotBeforeUpdate = class extends react.Component {
8
- getSnapshotBeforeUpdate() {
9
- if (this.props.enabled === false) {
21
+ getSnapshotBeforeUpdate(prevProps) {
22
+ if (this.props.enabled === false) return SKIP;
23
+ if (this.props.deps !== void 0 && checkIsDepsEqual_default(prevProps.deps, this.props.deps))
10
24
  return SKIP;
11
- }
12
25
  return this.props.capture();
13
26
  }
14
27
  componentDidUpdate(_prevProps, _prevState, snapshot) {
15
- if (this.props.enabled === false) return;
16
28
  if (snapshot === SKIP) return;
17
29
  this.props.apply(snapshot);
18
30
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SnapshotBeforeUpdate.tsx"],"names":["Component"],"mappings":";;;;;AAQA,IAAM,IAAA,0BAAc,4BAA4B,CAAA;AA0CzC,IAAM,oBAAA,GAAN,cAAgDA,eAAA,CAGrD;AAAA,EACS,uBAAA,GAA6B;AACpC,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,KAAA,EAAO;AAChC,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA,EAC5B;AAAA,EAES,kBAAA,CACP,UAAA,EACA,UAAA,EACA,QAAA,EACM;AACN,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,KAAA,EAAO;AAClC,IAAA,IAAK,aAAiC,IAAA,EAAM;AAE5C,IAAA,IAAA,CAAK,KAAA,CAAM,MAAM,QAAQ,CAAA;AAAA,EAC3B;AAAA,EAES,MAAA,GAAoB;AAC3B,IAAA,OAAO,IAAA,CAAK,MAAM,QAAA,IAAY,IAAA;AAAA,EAChC;AACF","file":"index.cjs","sourcesContent":["import type { ReactNode } from 'react';\nimport { Component } from 'react';\n\ntype EmptyState = Record<string, never>;\n\n/**\n * Symbol used to indicate that the snapshot should be skipped.\n */\nconst SKIP = Symbol('react-snapshot-bridge-skip');\ntype Skip = typeof SKIP;\n\nexport interface SnapshotBeforeUpdateProps<T> {\n /**\n * Runs synchronously **before** the DOM is mutated for this commit.\n * Whatever you return here is forwarded to {@link SnapshotBeforeUpdateProps.apply}.\n */\n capture: () => T;\n\n /**\n * Runs synchronously **after** the DOM is mutated, before the browser paints.\n * Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.\n */\n apply: (snapshot: T) => void;\n\n /**\n * When `false`, both `capture` and `apply` are skipped for that commit.\n * Evaluated against the **current** props at commit time, so toggling\n * `true` -> `false` skips both callbacks for that update, and `false` -> `true`\n * runs both as usual on the next update. Defaults to `true`.\n *\n * Updates that occur while disabled are not buffered or replayed later.\n */\n enabled?: boolean;\n\n /**\n * Optional children. When provided, this component acts as a wrapper and\n * renders its children. When omitted, it renders nothing and you can place\n * it as a sibling next to the elements you care about.\n */\n children?: ReactNode;\n}\n\n/**\n * Bridges React's class-only `getSnapshotBeforeUpdate` lifecycle into trees\n * built with function components.\n *\n * The component re-renders alongside its parent and uses the `getSnapshotBeforeUpdate`\n * / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}\n * before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.\n */\nexport class SnapshotBeforeUpdate<T = unknown> extends Component<\n SnapshotBeforeUpdateProps<T>,\n EmptyState\n> {\n override getSnapshotBeforeUpdate(): T {\n if (this.props.enabled === false) {\n return SKIP as unknown as T;\n }\n return this.props.capture();\n }\n\n override componentDidUpdate(\n _prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n _prevState: Readonly<EmptyState>,\n snapshot: T,\n ): void {\n if (this.props.enabled === false) return;\n if ((snapshot as unknown as Skip) === SKIP) return;\n\n this.props.apply(snapshot);\n }\n\n override render(): ReactNode {\n return this.props.children ?? null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/_utils/checkIsDepsEqual.ts","../src/SnapshotBeforeUpdate.tsx"],"names":["Component"],"mappings":";;;;;;;AAAA,SAAS,gBAAA,CACP,MACA,IAAA,EACS;AACT,EAAA,IAAI,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,MAAA,EAAW,OAAO,KAAA;AACrD,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,IAAA,CAAK,MAAA,EAAQ,OAAO,KAAA;AACxC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACpC,IAAA,IAAI,CAAC,MAAA,CAAO,EAAA,CAAG,IAAA,CAAK,CAAC,GAAG,IAAA,CAAK,CAAC,CAAC,CAAA,EAAG,OAAO,KAAA;AAAA,EAC3C;AACA,EAAA,OAAO,IAAA;AACT;AAEA,IAAO,wBAAA,GAAQ,gBAAA;;;ACHf,IAAM,IAAA,0BAAc,4BAA4B,CAAA;AAkDzC,IAAM,oBAAA,GAAN,cAAgDA,eAAA,CAIrD;AAAA,EACS,wBACP,SAAA,EACU;AACV,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,KAAA,EAAO,OAAO,IAAA;AAEzC,IAAA,IACE,IAAA,CAAK,MAAM,IAAA,KAAS,MAAA,IACpB,yBAAiB,SAAA,CAAU,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAEhD,MAAA,OAAO,IAAA;AAET,IAAA,OAAO,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA,EAC5B;AAAA,EAES,kBAAA,CACP,UAAA,EACA,UAAA,EACA,QAAA,EACM;AACN,IAAA,IAAI,aAAa,IAAA,EAAM;AAEvB,IAAA,IAAA,CAAK,KAAA,CAAM,MAAM,QAAQ,CAAA;AAAA,EAC3B;AAAA,EAES,MAAA,GAAoB;AAC3B,IAAA,OAAO,IAAA,CAAK,MAAM,QAAA,IAAY,IAAA;AAAA,EAChC;AACF","file":"index.cjs","sourcesContent":["function checkIsDepsEqual(\n prev: readonly unknown[] | undefined,\n next: readonly unknown[] | undefined,\n): boolean {\n if (prev === undefined || next === undefined) return false;\n if (prev.length !== next.length) return false;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return false;\n }\n return true;\n}\n\nexport default checkIsDepsEqual;\n","import type { ReactNode } from \"react\";\nimport { Component } from \"react\";\nimport checkIsDepsEqual from \"./_utils/checkIsDepsEqual\";\n\ntype EmptyState = Record<string, never>;\n\n/**\n * Symbol used to indicate that the snapshot should be skipped.\n */\nconst SKIP = Symbol(\"react-snapshot-bridge-skip\");\ntype Skip = typeof SKIP;\n\nexport interface SnapshotBeforeUpdateProps<T> {\n /**\n * Runs synchronously **before** the DOM is mutated for this commit.\n * Whatever you return here is forwarded to {@link SnapshotBeforeUpdateProps.apply}.\n */\n capture: () => T;\n\n /**\n * Runs synchronously **after** the DOM is mutated, before the browser paints.\n * Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.\n */\n apply: (snapshot: T) => void;\n\n /**\n * When `false`, both `capture` and `apply` are skipped for that commit.\n * Evaluated against the **current** props at commit time, so toggling\n * `true` -> `false` skips both callbacks for that update, and `false` -> `true`\n * runs both as usual on the next update. Defaults to `true`.\n *\n * Updates that occur while disabled are not buffered or replayed later.\n */\n enabled?: boolean;\n\n /**\n * Optional dependency array, compared shallowly (`Object.is`) against the\n * previous commit. When provided, both `capture` and `apply` run only on the\n * commit where at least one entry changed; commits with unchanged `deps` are\n * skipped. When omitted, both callbacks run on every update (default).\n */\n deps?: readonly unknown[];\n\n /**\n * Optional children. When provided, this component acts as a wrapper and\n * renders its children. When omitted, it renders nothing and you can place\n * it as a sibling next to the elements you care about.\n */\n children?: ReactNode;\n}\n\n/**\n * Bridges React's class-only `getSnapshotBeforeUpdate` lifecycle into trees\n * built with function components.\n *\n * The component re-renders alongside its parent and uses the `getSnapshotBeforeUpdate`\n * / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}\n * before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.\n */\nexport class SnapshotBeforeUpdate<T = unknown> extends Component<\n SnapshotBeforeUpdateProps<T>,\n EmptyState,\n T | Skip\n> {\n override getSnapshotBeforeUpdate(\n prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n ): T | Skip {\n if (this.props.enabled === false) return SKIP;\n\n if (\n this.props.deps !== undefined &&\n checkIsDepsEqual(prevProps.deps, this.props.deps)\n )\n return SKIP;\n\n return this.props.capture();\n }\n\n override componentDidUpdate(\n _prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n _prevState: Readonly<EmptyState>,\n snapshot: T | Skip,\n ): void {\n if (snapshot === SKIP) return;\n\n this.props.apply(snapshot);\n }\n\n override render(): ReactNode {\n return this.props.children ?? null;\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { Component, ReactNode } from 'react';
2
2
 
3
3
  type EmptyState = Record<string, never>;
4
+ /**
5
+ * Symbol used to indicate that the snapshot should be skipped.
6
+ */
7
+ declare const SKIP: unique symbol;
8
+ type Skip = typeof SKIP;
4
9
  interface SnapshotBeforeUpdateProps<T> {
5
10
  /**
6
11
  * Runs synchronously **before** the DOM is mutated for this commit.
@@ -21,6 +26,13 @@ interface SnapshotBeforeUpdateProps<T> {
21
26
  * Updates that occur while disabled are not buffered or replayed later.
22
27
  */
23
28
  enabled?: boolean;
29
+ /**
30
+ * Optional dependency array, compared shallowly (`Object.is`) against the
31
+ * previous commit. When provided, both `capture` and `apply` run only on the
32
+ * commit where at least one entry changed; commits with unchanged `deps` are
33
+ * skipped. When omitted, both callbacks run on every update (default).
34
+ */
35
+ deps?: readonly unknown[];
24
36
  /**
25
37
  * Optional children. When provided, this component acts as a wrapper and
26
38
  * renders its children. When omitted, it renders nothing and you can place
@@ -36,9 +48,9 @@ interface SnapshotBeforeUpdateProps<T> {
36
48
  * / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}
37
49
  * before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.
38
50
  */
39
- declare class SnapshotBeforeUpdate<T = unknown> extends Component<SnapshotBeforeUpdateProps<T>, EmptyState> {
40
- getSnapshotBeforeUpdate(): T;
41
- componentDidUpdate(_prevProps: Readonly<SnapshotBeforeUpdateProps<T>>, _prevState: Readonly<EmptyState>, snapshot: T): void;
51
+ declare class SnapshotBeforeUpdate<T = unknown> extends Component<SnapshotBeforeUpdateProps<T>, EmptyState, T | Skip> {
52
+ getSnapshotBeforeUpdate(prevProps: Readonly<SnapshotBeforeUpdateProps<T>>): T | Skip;
53
+ componentDidUpdate(_prevProps: Readonly<SnapshotBeforeUpdateProps<T>>, _prevState: Readonly<EmptyState>, snapshot: T | Skip): void;
42
54
  render(): ReactNode;
43
55
  }
44
56
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { Component, ReactNode } from 'react';
2
2
 
3
3
  type EmptyState = Record<string, never>;
4
+ /**
5
+ * Symbol used to indicate that the snapshot should be skipped.
6
+ */
7
+ declare const SKIP: unique symbol;
8
+ type Skip = typeof SKIP;
4
9
  interface SnapshotBeforeUpdateProps<T> {
5
10
  /**
6
11
  * Runs synchronously **before** the DOM is mutated for this commit.
@@ -21,6 +26,13 @@ interface SnapshotBeforeUpdateProps<T> {
21
26
  * Updates that occur while disabled are not buffered or replayed later.
22
27
  */
23
28
  enabled?: boolean;
29
+ /**
30
+ * Optional dependency array, compared shallowly (`Object.is`) against the
31
+ * previous commit. When provided, both `capture` and `apply` run only on the
32
+ * commit where at least one entry changed; commits with unchanged `deps` are
33
+ * skipped. When omitted, both callbacks run on every update (default).
34
+ */
35
+ deps?: readonly unknown[];
24
36
  /**
25
37
  * Optional children. When provided, this component acts as a wrapper and
26
38
  * renders its children. When omitted, it renders nothing and you can place
@@ -36,9 +48,9 @@ interface SnapshotBeforeUpdateProps<T> {
36
48
  * / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}
37
49
  * before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.
38
50
  */
39
- declare class SnapshotBeforeUpdate<T = unknown> extends Component<SnapshotBeforeUpdateProps<T>, EmptyState> {
40
- getSnapshotBeforeUpdate(): T;
41
- componentDidUpdate(_prevProps: Readonly<SnapshotBeforeUpdateProps<T>>, _prevState: Readonly<EmptyState>, snapshot: T): void;
51
+ declare class SnapshotBeforeUpdate<T = unknown> extends Component<SnapshotBeforeUpdateProps<T>, EmptyState, T | Skip> {
52
+ getSnapshotBeforeUpdate(prevProps: Readonly<SnapshotBeforeUpdateProps<T>>): T | Skip;
53
+ componentDidUpdate(_prevProps: Readonly<SnapshotBeforeUpdateProps<T>>, _prevState: Readonly<EmptyState>, snapshot: T | Skip): void;
42
54
  render(): ReactNode;
43
55
  }
44
56
 
package/dist/index.js CHANGED
@@ -1,16 +1,28 @@
1
1
  import { Component } from 'react';
2
2
 
3
+ // src/SnapshotBeforeUpdate.tsx
4
+
5
+ // src/_utils/checkIsDepsEqual.ts
6
+ function checkIsDepsEqual(prev, next) {
7
+ if (prev === void 0 || next === void 0) return false;
8
+ if (prev.length !== next.length) return false;
9
+ for (let i = 0; i < prev.length; i++) {
10
+ if (!Object.is(prev[i], next[i])) return false;
11
+ }
12
+ return true;
13
+ }
14
+ var checkIsDepsEqual_default = checkIsDepsEqual;
15
+
3
16
  // src/SnapshotBeforeUpdate.tsx
4
17
  var SKIP = /* @__PURE__ */ Symbol("react-snapshot-bridge-skip");
5
18
  var SnapshotBeforeUpdate = class extends Component {
6
- getSnapshotBeforeUpdate() {
7
- if (this.props.enabled === false) {
19
+ getSnapshotBeforeUpdate(prevProps) {
20
+ if (this.props.enabled === false) return SKIP;
21
+ if (this.props.deps !== void 0 && checkIsDepsEqual_default(prevProps.deps, this.props.deps))
8
22
  return SKIP;
9
- }
10
23
  return this.props.capture();
11
24
  }
12
25
  componentDidUpdate(_prevProps, _prevState, snapshot) {
13
- if (this.props.enabled === false) return;
14
26
  if (snapshot === SKIP) return;
15
27
  this.props.apply(snapshot);
16
28
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SnapshotBeforeUpdate.tsx"],"names":[],"mappings":";;;AAQA,IAAM,IAAA,0BAAc,4BAA4B,CAAA;AA0CzC,IAAM,oBAAA,GAAN,cAAgD,SAAA,CAGrD;AAAA,EACS,uBAAA,GAA6B;AACpC,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,KAAA,EAAO;AAChC,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA,EAC5B;AAAA,EAES,kBAAA,CACP,UAAA,EACA,UAAA,EACA,QAAA,EACM;AACN,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,KAAA,EAAO;AAClC,IAAA,IAAK,aAAiC,IAAA,EAAM;AAE5C,IAAA,IAAA,CAAK,KAAA,CAAM,MAAM,QAAQ,CAAA;AAAA,EAC3B;AAAA,EAES,MAAA,GAAoB;AAC3B,IAAA,OAAO,IAAA,CAAK,MAAM,QAAA,IAAY,IAAA;AAAA,EAChC;AACF","file":"index.js","sourcesContent":["import type { ReactNode } from 'react';\nimport { Component } from 'react';\n\ntype EmptyState = Record<string, never>;\n\n/**\n * Symbol used to indicate that the snapshot should be skipped.\n */\nconst SKIP = Symbol('react-snapshot-bridge-skip');\ntype Skip = typeof SKIP;\n\nexport interface SnapshotBeforeUpdateProps<T> {\n /**\n * Runs synchronously **before** the DOM is mutated for this commit.\n * Whatever you return here is forwarded to {@link SnapshotBeforeUpdateProps.apply}.\n */\n capture: () => T;\n\n /**\n * Runs synchronously **after** the DOM is mutated, before the browser paints.\n * Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.\n */\n apply: (snapshot: T) => void;\n\n /**\n * When `false`, both `capture` and `apply` are skipped for that commit.\n * Evaluated against the **current** props at commit time, so toggling\n * `true` -> `false` skips both callbacks for that update, and `false` -> `true`\n * runs both as usual on the next update. Defaults to `true`.\n *\n * Updates that occur while disabled are not buffered or replayed later.\n */\n enabled?: boolean;\n\n /**\n * Optional children. When provided, this component acts as a wrapper and\n * renders its children. When omitted, it renders nothing and you can place\n * it as a sibling next to the elements you care about.\n */\n children?: ReactNode;\n}\n\n/**\n * Bridges React's class-only `getSnapshotBeforeUpdate` lifecycle into trees\n * built with function components.\n *\n * The component re-renders alongside its parent and uses the `getSnapshotBeforeUpdate`\n * / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}\n * before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.\n */\nexport class SnapshotBeforeUpdate<T = unknown> extends Component<\n SnapshotBeforeUpdateProps<T>,\n EmptyState\n> {\n override getSnapshotBeforeUpdate(): T {\n if (this.props.enabled === false) {\n return SKIP as unknown as T;\n }\n return this.props.capture();\n }\n\n override componentDidUpdate(\n _prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n _prevState: Readonly<EmptyState>,\n snapshot: T,\n ): void {\n if (this.props.enabled === false) return;\n if ((snapshot as unknown as Skip) === SKIP) return;\n\n this.props.apply(snapshot);\n }\n\n override render(): ReactNode {\n return this.props.children ?? null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/_utils/checkIsDepsEqual.ts","../src/SnapshotBeforeUpdate.tsx"],"names":[],"mappings":";;;;;AAAA,SAAS,gBAAA,CACP,MACA,IAAA,EACS;AACT,EAAA,IAAI,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,MAAA,EAAW,OAAO,KAAA;AACrD,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,IAAA,CAAK,MAAA,EAAQ,OAAO,KAAA;AACxC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACpC,IAAA,IAAI,CAAC,MAAA,CAAO,EAAA,CAAG,IAAA,CAAK,CAAC,GAAG,IAAA,CAAK,CAAC,CAAC,CAAA,EAAG,OAAO,KAAA;AAAA,EAC3C;AACA,EAAA,OAAO,IAAA;AACT;AAEA,IAAO,wBAAA,GAAQ,gBAAA;;;ACHf,IAAM,IAAA,0BAAc,4BAA4B,CAAA;AAkDzC,IAAM,oBAAA,GAAN,cAAgD,SAAA,CAIrD;AAAA,EACS,wBACP,SAAA,EACU;AACV,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,KAAA,EAAO,OAAO,IAAA;AAEzC,IAAA,IACE,IAAA,CAAK,MAAM,IAAA,KAAS,MAAA,IACpB,yBAAiB,SAAA,CAAU,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAEhD,MAAA,OAAO,IAAA;AAET,IAAA,OAAO,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA,EAC5B;AAAA,EAES,kBAAA,CACP,UAAA,EACA,UAAA,EACA,QAAA,EACM;AACN,IAAA,IAAI,aAAa,IAAA,EAAM;AAEvB,IAAA,IAAA,CAAK,KAAA,CAAM,MAAM,QAAQ,CAAA;AAAA,EAC3B;AAAA,EAES,MAAA,GAAoB;AAC3B,IAAA,OAAO,IAAA,CAAK,MAAM,QAAA,IAAY,IAAA;AAAA,EAChC;AACF","file":"index.js","sourcesContent":["function checkIsDepsEqual(\n prev: readonly unknown[] | undefined,\n next: readonly unknown[] | undefined,\n): boolean {\n if (prev === undefined || next === undefined) return false;\n if (prev.length !== next.length) return false;\n for (let i = 0; i < prev.length; i++) {\n if (!Object.is(prev[i], next[i])) return false;\n }\n return true;\n}\n\nexport default checkIsDepsEqual;\n","import type { ReactNode } from \"react\";\nimport { Component } from \"react\";\nimport checkIsDepsEqual from \"./_utils/checkIsDepsEqual\";\n\ntype EmptyState = Record<string, never>;\n\n/**\n * Symbol used to indicate that the snapshot should be skipped.\n */\nconst SKIP = Symbol(\"react-snapshot-bridge-skip\");\ntype Skip = typeof SKIP;\n\nexport interface SnapshotBeforeUpdateProps<T> {\n /**\n * Runs synchronously **before** the DOM is mutated for this commit.\n * Whatever you return here is forwarded to {@link SnapshotBeforeUpdateProps.apply}.\n */\n capture: () => T;\n\n /**\n * Runs synchronously **after** the DOM is mutated, before the browser paints.\n * Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.\n */\n apply: (snapshot: T) => void;\n\n /**\n * When `false`, both `capture` and `apply` are skipped for that commit.\n * Evaluated against the **current** props at commit time, so toggling\n * `true` -> `false` skips both callbacks for that update, and `false` -> `true`\n * runs both as usual on the next update. Defaults to `true`.\n *\n * Updates that occur while disabled are not buffered or replayed later.\n */\n enabled?: boolean;\n\n /**\n * Optional dependency array, compared shallowly (`Object.is`) against the\n * previous commit. When provided, both `capture` and `apply` run only on the\n * commit where at least one entry changed; commits with unchanged `deps` are\n * skipped. When omitted, both callbacks run on every update (default).\n */\n deps?: readonly unknown[];\n\n /**\n * Optional children. When provided, this component acts as a wrapper and\n * renders its children. When omitted, it renders nothing and you can place\n * it as a sibling next to the elements you care about.\n */\n children?: ReactNode;\n}\n\n/**\n * Bridges React's class-only `getSnapshotBeforeUpdate` lifecycle into trees\n * built with function components.\n *\n * The component re-renders alongside its parent and uses the `getSnapshotBeforeUpdate`\n * / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}\n * before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.\n */\nexport class SnapshotBeforeUpdate<T = unknown> extends Component<\n SnapshotBeforeUpdateProps<T>,\n EmptyState,\n T | Skip\n> {\n override getSnapshotBeforeUpdate(\n prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n ): T | Skip {\n if (this.props.enabled === false) return SKIP;\n\n if (\n this.props.deps !== undefined &&\n checkIsDepsEqual(prevProps.deps, this.props.deps)\n )\n return SKIP;\n\n return this.props.capture();\n }\n\n override componentDidUpdate(\n _prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n _prevState: Readonly<EmptyState>,\n snapshot: T | Skip,\n ): void {\n if (snapshot === SKIP) return;\n\n this.props.apply(snapshot);\n }\n\n override render(): ReactNode {\n return this.props.children ?? null;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-snapshot-bridge",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Use React's class-only `getSnapshotBeforeUpdate` lifecycle from function components.",
5
5
  "keywords": [
6
6
  "react",