react-snapshot-bridge 0.1.0 → 1.1.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 +81 -25
- 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 +18 -14
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# react-snapshot-bridge
|
|
2
2
|
|
|
3
|
-
> Use React's class-only `getSnapshotBeforeUpdate` lifecycle from **function components
|
|
3
|
+
> Use React's class-only `getSnapshotBeforeUpdate` lifecycle from **function components**.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/react-snapshot-bridge)
|
|
6
6
|
[](https://bundlephobia.com/package/react-snapshot-bridge)
|
|
7
|
+
[](https://react-snapshot-bridge.vercel.app/)
|
|
7
8
|
[](./LICENSE)
|
|
8
9
|
|
|
9
10
|
## Why
|
|
@@ -34,16 +35,16 @@ Peer dependency: `react >= 16.3` (when `getSnapshotBeforeUpdate` was introduced)
|
|
|
34
35
|
## Quick start — preserve scroll on prepend
|
|
35
36
|
|
|
36
37
|
```tsx
|
|
37
|
-
import { useRef, useState } from
|
|
38
|
-
import { SnapshotBeforeUpdate } from
|
|
38
|
+
import { useRef, useState } from "react";
|
|
39
|
+
import { SnapshotBeforeUpdate } from "react-snapshot-bridge";
|
|
39
40
|
|
|
40
41
|
function ChatList() {
|
|
41
42
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
42
|
-
const [messages, setMessages] = useState<string[]>([
|
|
43
|
+
const [messages, setMessages] = useState<string[]>(["hi"]);
|
|
43
44
|
|
|
44
45
|
return (
|
|
45
46
|
<>
|
|
46
|
-
<button onClick={() => setMessages((m) => [
|
|
47
|
+
<button onClick={() => setMessages((m) => ["new message", ...m])}>
|
|
47
48
|
Prepend
|
|
48
49
|
</button>
|
|
49
50
|
|
|
@@ -57,7 +58,7 @@ function ChatList() {
|
|
|
57
58
|
el.scrollTop = el.scrollHeight - prevDistanceFromBottom;
|
|
58
59
|
}}
|
|
59
60
|
>
|
|
60
|
-
<div ref={scrollerRef} style={{ height: 200, overflow:
|
|
61
|
+
<div ref={scrollerRef} style={{ height: 200, overflow: "auto" }}>
|
|
61
62
|
{messages.map((msg, i) => (
|
|
62
63
|
<div key={i}>{msg}</div>
|
|
63
64
|
))}
|
|
@@ -77,46 +78,50 @@ Without the wrapper, every prepend visually shifts the user's content down. With
|
|
|
77
78
|
The example below combines two ideas at once:
|
|
78
79
|
|
|
79
80
|
1. Capture two DOM-derived values (`distanceFromBottom`, `wasAtBottom`) into one snapshot.
|
|
80
|
-
2.
|
|
81
|
+
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
82
|
|
|
82
83
|
```tsx
|
|
83
|
-
import { useRef, useState } from
|
|
84
|
-
import { SnapshotBeforeUpdate } from
|
|
84
|
+
import { useRef, useState } from "react";
|
|
85
|
+
import { SnapshotBeforeUpdate } from "react-snapshot-bridge";
|
|
85
86
|
|
|
86
87
|
type Snap = {
|
|
87
88
|
distanceFromBottom: number;
|
|
88
89
|
wasAtBottom: boolean;
|
|
89
|
-
|
|
90
|
+
previousMessageCount: number;
|
|
90
91
|
};
|
|
91
92
|
|
|
92
93
|
function ChatList({ messages }: { messages: string[] }) {
|
|
93
94
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
95
|
+
const prevMessageCountRef = useRef(messages.length);
|
|
94
96
|
|
|
95
97
|
return (
|
|
96
98
|
<SnapshotBeforeUpdate<Snap>
|
|
97
99
|
capture={() => {
|
|
98
100
|
const el = scrollerRef.current!;
|
|
99
|
-
const distanceFromBottom =
|
|
101
|
+
const distanceFromBottom =
|
|
102
|
+
el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
100
103
|
return {
|
|
101
104
|
distanceFromBottom,
|
|
102
105
|
wasAtBottom: distanceFromBottom < 16,
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
prevCount: messages.length,
|
|
106
|
+
// Still the count from the end of the last `apply` (or initial render).
|
|
107
|
+
previousMessageCount: prevMessageCountRef.current,
|
|
106
108
|
};
|
|
107
109
|
}}
|
|
108
|
-
apply={({ distanceFromBottom, wasAtBottom,
|
|
110
|
+
apply={({ distanceFromBottom, wasAtBottom, previousMessageCount }) => {
|
|
109
111
|
const el = scrollerRef.current!;
|
|
110
|
-
const grewAtTop = messages.length >
|
|
112
|
+
const grewAtTop = messages.length > previousMessageCount;
|
|
111
113
|
|
|
112
114
|
if (wasAtBottom) {
|
|
113
115
|
el.scrollTop = el.scrollHeight; // stick to bottom for new arrivals
|
|
114
116
|
} else if (grewAtTop) {
|
|
115
117
|
el.scrollTop = el.scrollHeight - distanceFromBottom; // anchor existing content
|
|
116
118
|
}
|
|
119
|
+
|
|
120
|
+
// Next `capture` reads this as the "previous" count for that commit.
|
|
121
|
+
prevMessageCountRef.current = messages.length;
|
|
117
122
|
}}
|
|
118
123
|
>
|
|
119
|
-
<div ref={scrollerRef} style={{ height: 200, overflow:
|
|
124
|
+
<div ref={scrollerRef} style={{ height: 200, overflow: "auto" }}>
|
|
120
125
|
{messages.map((msg, i) => (
|
|
121
126
|
<div key={i}>{msg}</div>
|
|
122
127
|
))}
|
|
@@ -126,20 +131,70 @@ function ChatList({ messages }: { messages: string[] }) {
|
|
|
126
131
|
}
|
|
127
132
|
```
|
|
128
133
|
|
|
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.
|
|
134
|
+
> 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
135
|
|
|
131
136
|
## API
|
|
132
137
|
|
|
133
|
-
### `<SnapshotBeforeUpdate<T> capture apply
|
|
138
|
+
### `<SnapshotBeforeUpdate<T> capture apply enabled?>{children?}</SnapshotBeforeUpdate>`
|
|
134
139
|
|
|
135
|
-
| Prop | Type
|
|
136
|
-
| ---------- |
|
|
137
|
-
| `capture` | `() => T`
|
|
138
|
-
| `apply` | `(snapshot: T) => void`
|
|
139
|
-
| `
|
|
140
|
+
| Prop | Type | Required | Description |
|
|
141
|
+
| ---------- | ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| `capture` | `() => T` | yes | Called synchronously **before** DOM mutation. Return value is forwarded to `apply`. |
|
|
143
|
+
| `apply` | `(snapshot: T) => void` | yes | Called synchronously **after** DOM mutation, before paint. Receives the value from `capture`. |
|
|
144
|
+
| `enabled` | `boolean` | no | Defaults to `true`. When `false`, both `capture` and `apply` are skipped for that commit. Missed updates are not replayed. |
|
|
145
|
+
| `children` | `React.ReactNode` | no | Optional. When provided, rendered as-is (wrapper pattern). When omitted, renders nothing. |
|
|
140
146
|
|
|
141
147
|
The generic `T` is inferred from `capture`'s return type — you usually don't need to specify it.
|
|
142
148
|
|
|
149
|
+
## Where to place it
|
|
150
|
+
|
|
151
|
+
`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.
|
|
152
|
+
|
|
153
|
+
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.
|
|
154
|
+
|
|
155
|
+
> The deciding factor is "which component re-renders alongside the bridge", not "wrapper vs sibling".
|
|
156
|
+
|
|
157
|
+
### Recommended 1 — Wrapper
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
function ChatList({ messages }: { messages: string[] }) {
|
|
161
|
+
return (
|
|
162
|
+
<SnapshotBeforeUpdate capture={...} apply={...}>
|
|
163
|
+
<div>{/* the DOM you actually care about */}</div>
|
|
164
|
+
</SnapshotBeforeUpdate>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Recommended 2 — Sibling of the DOM, inside the same component
|
|
170
|
+
|
|
171
|
+
Useful when wrapping is awkward (e.g. you already have multiple siblings inside a Fragment).
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
function ChatList({ messages }: { messages: string[] }) {
|
|
175
|
+
return (
|
|
176
|
+
<>
|
|
177
|
+
<div>{/* the DOM you actually care about */}</div>
|
|
178
|
+
<SnapshotBeforeUpdate capture={...} apply={...} />
|
|
179
|
+
</>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Not recommended — Sibling of the component you want to track
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
function Parent() {
|
|
188
|
+
return (
|
|
189
|
+
<>
|
|
190
|
+
<ChatList messages={messages} />
|
|
191
|
+
{/* Wrong: standalone updates inside ChatList may not be picked up */}
|
|
192
|
+
<SnapshotBeforeUpdate capture={...} apply={...} />
|
|
193
|
+
</>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
143
198
|
## How it works
|
|
144
199
|
|
|
145
200
|
React's commit phase runs in this order:
|
|
@@ -158,7 +213,8 @@ The captured value is forwarded to `componentDidUpdate`, which calls `apply`. Fr
|
|
|
158
213
|
## Caveats
|
|
159
214
|
|
|
160
215
|
- **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
|
-
- **Requires a re-render.** If a `React.memo` (or `shouldComponentUpdate`) ancestor short-circuits the render, the bridge
|
|
216
|
+
- **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).
|
|
217
|
+
- **`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
218
|
- **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
219
|
- **SSR safe.** Lifecycle methods don't run during `renderToString` / `renderToPipeableStream`, so the bridge is a no-op on the server.
|
|
164
220
|
- **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 { Component } from \"react\";\nimport type { ReactNode } 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 { Component } from \"react\";\nimport type { ReactNode } 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": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Use React's class-only `getSnapshotBeforeUpdate` lifecycle from function components.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"getSnapshotBeforeUpdate",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"url": "https://github.com/ksr20612/react-snapshot-bridge.git",
|
|
22
22
|
"directory": "packages/react-snapshot-bridge"
|
|
23
23
|
},
|
|
24
|
-
"homepage": "https://
|
|
24
|
+
"homepage": "https://react-snapshot-bridge.vercel.app",
|
|
25
25
|
"bugs": {
|
|
26
26
|
"url": "https://github.com/ksr20612/react-snapshot-bridge/issues"
|
|
27
27
|
},
|
|
@@ -41,23 +41,20 @@
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
|
-
"files": [
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
45
49
|
"sideEffects": false,
|
|
46
50
|
"publishConfig": {
|
|
47
51
|
"access": "public",
|
|
48
|
-
"registry": "https://registry.npmjs.org/"
|
|
52
|
+
"registry": "https://registry.npmjs.org/",
|
|
53
|
+
"provenance": true
|
|
49
54
|
},
|
|
50
55
|
"engines": {
|
|
51
56
|
"node": ">=20"
|
|
52
57
|
},
|
|
53
|
-
"scripts": {
|
|
54
|
-
"build": "tsup",
|
|
55
|
-
"dev": "tsup --watch",
|
|
56
|
-
"test": "vitest run",
|
|
57
|
-
"test:watch": "vitest",
|
|
58
|
-
"typecheck": "tsc --noEmit",
|
|
59
|
-
"prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
|
|
60
|
-
},
|
|
61
58
|
"peerDependencies": {
|
|
62
59
|
"react": ">=16.3.0"
|
|
63
60
|
},
|
|
@@ -71,5 +68,12 @@
|
|
|
71
68
|
"tsup": "^8.3.5",
|
|
72
69
|
"typescript": "^5.7.2",
|
|
73
70
|
"vitest": "^2.1.8"
|
|
71
|
+
},
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "tsup",
|
|
74
|
+
"dev": "tsup --watch",
|
|
75
|
+
"test": "vitest run",
|
|
76
|
+
"test:watch": "vitest",
|
|
77
|
+
"typecheck": "tsc --noEmit"
|
|
74
78
|
}
|
|
75
|
-
}
|
|
79
|
+
}
|