react-snapshot-bridge 1.0.0 → 1.1.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.
- package/README.md +91 -26
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://react-snapshot-bridge.vercel.app/logo.png"
|
|
4
|
+
alt="react-snapshot-bridge logo"
|
|
5
|
+
width="500"
|
|
6
|
+
/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
1
9
|
# react-snapshot-bridge
|
|
2
10
|
|
|
3
|
-
> Use React's class-only `getSnapshotBeforeUpdate` lifecycle from **function components
|
|
11
|
+
> Use React's class-only `getSnapshotBeforeUpdate` lifecycle from **function components**.
|
|
4
12
|
|
|
5
13
|
[](https://www.npmjs.com/package/react-snapshot-bridge)
|
|
6
14
|
[](https://bundlephobia.com/package/react-snapshot-bridge)
|
|
15
|
+
[](https://react-snapshot-bridge.vercel.app/)
|
|
7
16
|
[](./LICENSE)
|
|
8
17
|
|
|
9
18
|
## Why
|
|
@@ -34,16 +43,16 @@ Peer dependency: `react >= 16.3` (when `getSnapshotBeforeUpdate` was introduced)
|
|
|
34
43
|
## Quick start — preserve scroll on prepend
|
|
35
44
|
|
|
36
45
|
```tsx
|
|
37
|
-
import { useRef, useState } from
|
|
38
|
-
import { SnapshotBeforeUpdate } from
|
|
46
|
+
import { useRef, useState } from "react";
|
|
47
|
+
import { SnapshotBeforeUpdate } from "react-snapshot-bridge";
|
|
39
48
|
|
|
40
49
|
function ChatList() {
|
|
41
50
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
42
|
-
const [messages, setMessages] = useState<string[]>([
|
|
51
|
+
const [messages, setMessages] = useState<string[]>(["hi"]);
|
|
43
52
|
|
|
44
53
|
return (
|
|
45
54
|
<>
|
|
46
|
-
<button onClick={() => setMessages((m) => [
|
|
55
|
+
<button onClick={() => setMessages((m) => ["new message", ...m])}>
|
|
47
56
|
Prepend
|
|
48
57
|
</button>
|
|
49
58
|
|
|
@@ -57,7 +66,7 @@ function ChatList() {
|
|
|
57
66
|
el.scrollTop = el.scrollHeight - prevDistanceFromBottom;
|
|
58
67
|
}}
|
|
59
68
|
>
|
|
60
|
-
<div ref={scrollerRef} style={{ height: 200, overflow:
|
|
69
|
+
<div ref={scrollerRef} style={{ height: 200, overflow: "auto" }}>
|
|
61
70
|
{messages.map((msg, i) => (
|
|
62
71
|
<div key={i}>{msg}</div>
|
|
63
72
|
))}
|
|
@@ -77,46 +86,50 @@ Without the wrapper, every prepend visually shifts the user's content down. With
|
|
|
77
86
|
The example below combines two ideas at once:
|
|
78
87
|
|
|
79
88
|
1. Capture two DOM-derived values (`distanceFromBottom`, `wasAtBottom`) into one snapshot.
|
|
80
|
-
2.
|
|
89
|
+
2. Track the **previous** message count with a ref (synced at the end of `apply`), read the **current** `messages` in `apply` via closure, and read DOM in `capture` before the commit mutates it.
|
|
81
90
|
|
|
82
91
|
```tsx
|
|
83
|
-
import { useRef, useState } from
|
|
84
|
-
import { SnapshotBeforeUpdate } from
|
|
92
|
+
import { useRef, useState } from "react";
|
|
93
|
+
import { SnapshotBeforeUpdate } from "react-snapshot-bridge";
|
|
85
94
|
|
|
86
95
|
type Snap = {
|
|
87
96
|
distanceFromBottom: number;
|
|
88
97
|
wasAtBottom: boolean;
|
|
89
|
-
|
|
98
|
+
previousMessageCount: number;
|
|
90
99
|
};
|
|
91
100
|
|
|
92
101
|
function ChatList({ messages }: { messages: string[] }) {
|
|
93
102
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
103
|
+
const prevMessageCountRef = useRef(messages.length);
|
|
94
104
|
|
|
95
105
|
return (
|
|
96
106
|
<SnapshotBeforeUpdate<Snap>
|
|
97
107
|
capture={() => {
|
|
98
108
|
const el = scrollerRef.current!;
|
|
99
|
-
const distanceFromBottom =
|
|
109
|
+
const distanceFromBottom =
|
|
110
|
+
el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
100
111
|
return {
|
|
101
112
|
distanceFromBottom,
|
|
102
113
|
wasAtBottom: distanceFromBottom < 16,
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
prevCount: messages.length,
|
|
114
|
+
// Still the count from the end of the last `apply` (or initial render).
|
|
115
|
+
previousMessageCount: prevMessageCountRef.current,
|
|
106
116
|
};
|
|
107
117
|
}}
|
|
108
|
-
apply={({ distanceFromBottom, wasAtBottom,
|
|
118
|
+
apply={({ distanceFromBottom, wasAtBottom, previousMessageCount }) => {
|
|
109
119
|
const el = scrollerRef.current!;
|
|
110
|
-
const grewAtTop = messages.length >
|
|
120
|
+
const grewAtTop = messages.length > previousMessageCount;
|
|
111
121
|
|
|
112
122
|
if (wasAtBottom) {
|
|
113
123
|
el.scrollTop = el.scrollHeight; // stick to bottom for new arrivals
|
|
114
124
|
} else if (grewAtTop) {
|
|
115
125
|
el.scrollTop = el.scrollHeight - distanceFromBottom; // anchor existing content
|
|
116
126
|
}
|
|
127
|
+
|
|
128
|
+
// Next `capture` reads this as the "previous" count for that commit.
|
|
129
|
+
prevMessageCountRef.current = messages.length;
|
|
117
130
|
}}
|
|
118
131
|
>
|
|
119
|
-
<div ref={scrollerRef} style={{ height: 200, overflow:
|
|
132
|
+
<div ref={scrollerRef} style={{ height: 200, overflow: "auto" }}>
|
|
120
133
|
{messages.map((msg, i) => (
|
|
121
134
|
<div key={i}>{msg}</div>
|
|
122
135
|
))}
|
|
@@ -126,20 +139,70 @@ function ChatList({ messages }: { messages: string[] }) {
|
|
|
126
139
|
}
|
|
127
140
|
```
|
|
128
141
|
|
|
129
|
-
> Why not pass `prevProps` / `prevState` directly into `apply`? Because the wrapper would only see its own internal props (`{ capture, apply, children }`) and an empty state — neither has anything to do with your component.
|
|
142
|
+
> Why not pass `prevProps` / `prevState` directly into `apply`? Because the wrapper would only see its own internal props (`{ capture, apply, children }`) and an empty state — neither has anything to do with your component. Use closure for values you only need after the DOM updates, and a ref when you need a stable "previous" value across commits.
|
|
130
143
|
|
|
131
144
|
## API
|
|
132
145
|
|
|
133
|
-
### `<SnapshotBeforeUpdate<T> capture apply
|
|
146
|
+
### `<SnapshotBeforeUpdate<T> capture apply enabled?>{children?}</SnapshotBeforeUpdate>`
|
|
134
147
|
|
|
135
|
-
| Prop | Type
|
|
136
|
-
| ---------- |
|
|
137
|
-
| `capture` | `() => T`
|
|
138
|
-
| `apply` | `(snapshot: T) => void`
|
|
139
|
-
| `
|
|
148
|
+
| Prop | Type | Required | Description |
|
|
149
|
+
| ---------- | ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
150
|
+
| `capture` | `() => T` | yes | Called synchronously **before** DOM mutation. Return value is forwarded to `apply`. |
|
|
151
|
+
| `apply` | `(snapshot: T) => void` | yes | Called synchronously **after** DOM mutation, before paint. Receives the value from `capture`. |
|
|
152
|
+
| `enabled` | `boolean` | no | Defaults to `true`. When `false`, both `capture` and `apply` are skipped for that commit. Missed updates are not replayed. |
|
|
153
|
+
| `children` | `React.ReactNode` | no | Optional. When provided, rendered as-is (wrapper pattern). When omitted, renders nothing. |
|
|
140
154
|
|
|
141
155
|
The generic `T` is inferred from `capture`'s return type — you usually don't need to specify it.
|
|
142
156
|
|
|
157
|
+
## Where to place it
|
|
158
|
+
|
|
159
|
+
`SnapshotBeforeUpdate` runs its lifecycle **only when its own fiber updates**. So if you want to observe `getSnapshotBeforeUpdate` of a particular component, the bridge has to live **inside that component's render output** — that way, every time the component re-renders, the bridge re-renders with it.
|
|
160
|
+
|
|
161
|
+
Within the same component, both wrapping the DOM as `children` and placing the bridge as a **sibling** of the DOM you care about work perfectly. What does _not_ work reliably is putting the bridge as a sibling of **another component** you want to track: when that component updates on its own (without its parent re-rendering), the bridge will not fire.
|
|
162
|
+
|
|
163
|
+
> The deciding factor is "which component re-renders alongside the bridge", not "wrapper vs sibling".
|
|
164
|
+
|
|
165
|
+
### Recommended 1 — Wrapper
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
function ChatList({ messages }: { messages: string[] }) {
|
|
169
|
+
return (
|
|
170
|
+
<SnapshotBeforeUpdate capture={...} apply={...}>
|
|
171
|
+
<div>{/* the DOM you actually care about */}</div>
|
|
172
|
+
</SnapshotBeforeUpdate>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Recommended 2 — Sibling of the DOM, inside the same component
|
|
178
|
+
|
|
179
|
+
Useful when wrapping is awkward (e.g. you already have multiple siblings inside a Fragment).
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
function ChatList({ messages }: { messages: string[] }) {
|
|
183
|
+
return (
|
|
184
|
+
<>
|
|
185
|
+
<div>{/* the DOM you actually care about */}</div>
|
|
186
|
+
<SnapshotBeforeUpdate capture={...} apply={...} />
|
|
187
|
+
</>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Not recommended — Sibling of the component you want to track
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
function Parent() {
|
|
196
|
+
return (
|
|
197
|
+
<>
|
|
198
|
+
<ChatList messages={messages} />
|
|
199
|
+
{/* Wrong: standalone updates inside ChatList may not be picked up */}
|
|
200
|
+
<SnapshotBeforeUpdate capture={...} apply={...} />
|
|
201
|
+
</>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
143
206
|
## How it works
|
|
144
207
|
|
|
145
208
|
React's commit phase runs in this order:
|
|
@@ -157,8 +220,10 @@ The captured value is forwarded to `componentDidUpdate`, which calls `apply`. Fr
|
|
|
157
220
|
|
|
158
221
|
## Caveats
|
|
159
222
|
|
|
160
|
-
- **No call on initial mount.** Like the underlying class lifecycle, neither `capture` nor `apply` runs the first time the component appears — only on subsequent updates.
|
|
161
|
-
- **
|
|
223
|
+
- **No call on initial mount.** Like the underlying class lifecycle, neither `capture` nor `apply` runs the first time the component appears — only on subsequent updates. `getSnapshotBeforeUpdate` is **update-only**, so you cannot use this bridge to snapshot the DOM immediately before the component's **initial** appearance on screen. When someone says "before first paint," clarify whether they mean the **first mount paint** (not covered by this lifecycle) or a **later update commit** (see next bullet).
|
|
224
|
+
- **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
|
+
- **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
|
+
- **`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.
|
|
162
227
|
- **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.
|
|
163
228
|
- **SSR safe.** Lifecycle methods don't run during `renderToString` / `renderToPipeableStream`, so the bridge is a no-op on the server.
|
|
164
229
|
- **React 19.** Class components and `getSnapshotBeforeUpdate` are not deprecated in React 19. This library remains compatible.
|
package/dist/index.cjs
CHANGED
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
|
|
5
5
|
// src/SnapshotBeforeUpdate.tsx
|
|
6
|
+
var SKIP = /* @__PURE__ */ Symbol("react-snapshot-bridge-skip");
|
|
6
7
|
var SnapshotBeforeUpdate = class extends react.Component {
|
|
7
8
|
getSnapshotBeforeUpdate() {
|
|
9
|
+
if (this.props.enabled === false) {
|
|
10
|
+
return SKIP;
|
|
11
|
+
}
|
|
8
12
|
return this.props.capture();
|
|
9
13
|
}
|
|
10
14
|
componentDidUpdate(_prevProps, _prevState, snapshot) {
|
|
15
|
+
if (this.props.enabled === false) return;
|
|
16
|
+
if (snapshot === SKIP) return;
|
|
11
17
|
this.props.apply(snapshot);
|
|
12
18
|
}
|
|
13
19
|
render() {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SnapshotBeforeUpdate.tsx"],"names":["Component"],"mappings":";;;;;
|
|
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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -12,6 +12,15 @@ interface SnapshotBeforeUpdateProps<T> {
|
|
|
12
12
|
* Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.
|
|
13
13
|
*/
|
|
14
14
|
apply: (snapshot: T) => void;
|
|
15
|
+
/**
|
|
16
|
+
* When `false`, both `capture` and `apply` are skipped for that commit.
|
|
17
|
+
* Evaluated against the **current** props at commit time, so toggling
|
|
18
|
+
* `true` -> `false` skips both callbacks for that update, and `false` -> `true`
|
|
19
|
+
* runs both as usual on the next update. Defaults to `true`.
|
|
20
|
+
*
|
|
21
|
+
* Updates that occur while disabled are not buffered or replayed later.
|
|
22
|
+
*/
|
|
23
|
+
enabled?: boolean;
|
|
15
24
|
/**
|
|
16
25
|
* Optional children. When provided, this component acts as a wrapper and
|
|
17
26
|
* renders its children. When omitted, it renders nothing and you can place
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,15 @@ interface SnapshotBeforeUpdateProps<T> {
|
|
|
12
12
|
* Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.
|
|
13
13
|
*/
|
|
14
14
|
apply: (snapshot: T) => void;
|
|
15
|
+
/**
|
|
16
|
+
* When `false`, both `capture` and `apply` are skipped for that commit.
|
|
17
|
+
* Evaluated against the **current** props at commit time, so toggling
|
|
18
|
+
* `true` -> `false` skips both callbacks for that update, and `false` -> `true`
|
|
19
|
+
* runs both as usual on the next update. Defaults to `true`.
|
|
20
|
+
*
|
|
21
|
+
* Updates that occur while disabled are not buffered or replayed later.
|
|
22
|
+
*/
|
|
23
|
+
enabled?: boolean;
|
|
15
24
|
/**
|
|
16
25
|
* Optional children. When provided, this component acts as a wrapper and
|
|
17
26
|
* renders its children. When omitted, it renders nothing and you can place
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Component } from 'react';
|
|
2
2
|
|
|
3
3
|
// src/SnapshotBeforeUpdate.tsx
|
|
4
|
+
var SKIP = /* @__PURE__ */ Symbol("react-snapshot-bridge-skip");
|
|
4
5
|
var SnapshotBeforeUpdate = class extends Component {
|
|
5
6
|
getSnapshotBeforeUpdate() {
|
|
7
|
+
if (this.props.enabled === false) {
|
|
8
|
+
return SKIP;
|
|
9
|
+
}
|
|
6
10
|
return this.props.capture();
|
|
7
11
|
}
|
|
8
12
|
componentDidUpdate(_prevProps, _prevState, snapshot) {
|
|
13
|
+
if (this.props.enabled === false) return;
|
|
14
|
+
if (snapshot === SKIP) return;
|
|
9
15
|
this.props.apply(snapshot);
|
|
10
16
|
}
|
|
11
17
|
render() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SnapshotBeforeUpdate.tsx"],"names":[],"mappings":";;;
|
|
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"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-snapshot-bridge",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "Use React's class-only `getSnapshotBeforeUpdate` lifecycle from function components.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"getSnapshotBeforeUpdate",
|