react-snapshot-bridge 0.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/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/index.cjs +20 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +36 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ethan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# react-snapshot-bridge
|
|
2
|
+
|
|
3
|
+
> Use React's class-only `getSnapshotBeforeUpdate` lifecycle from **function components**, via a single 30-line wrapper.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/react-snapshot-bridge)
|
|
6
|
+
[](https://bundlephobia.com/package/react-snapshot-bridge)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
`useLayoutEffect` runs **after** the DOM is mutated, so by the time it fires the previous DOM state — old `scrollTop`, prior selection, prior dimensions — is already gone.
|
|
12
|
+
|
|
13
|
+
`getSnapshotBeforeUpdate` is the React lifecycle that runs **right before** the mutation, returns a "snapshot" value, and forwards it to `componentDidUpdate`. It exists only on class components, and there is no built-in hook equivalent.
|
|
14
|
+
|
|
15
|
+
This library bridges that gap with a single drop-in wrapper. Use it when you need to:
|
|
16
|
+
|
|
17
|
+
- Keep the visible content anchored when items are inserted **above** a scroll container (chat lists, feeds)
|
|
18
|
+
- Preserve text selection or caret position across re-renders
|
|
19
|
+
- Capture element dimensions for FLIP-style animations
|
|
20
|
+
- Read any "before commit" DOM state and apply it after the commit
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add react-snapshot-bridge
|
|
26
|
+
# or
|
|
27
|
+
npm i react-snapshot-bridge
|
|
28
|
+
# or
|
|
29
|
+
yarn add react-snapshot-bridge
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Peer dependency: `react >= 16.3` (when `getSnapshotBeforeUpdate` was introduced).
|
|
33
|
+
|
|
34
|
+
## Quick start — preserve scroll on prepend
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { useRef, useState } from 'react';
|
|
38
|
+
import { SnapshotBeforeUpdate } from 'react-snapshot-bridge';
|
|
39
|
+
|
|
40
|
+
function ChatList() {
|
|
41
|
+
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
42
|
+
const [messages, setMessages] = useState<string[]>(['hi']);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<button onClick={() => setMessages((m) => ['new message', ...m])}>
|
|
47
|
+
Prepend
|
|
48
|
+
</button>
|
|
49
|
+
|
|
50
|
+
<SnapshotBeforeUpdate<number>
|
|
51
|
+
capture={() => {
|
|
52
|
+
const el = scrollerRef.current!;
|
|
53
|
+
return el.scrollHeight - el.scrollTop;
|
|
54
|
+
}}
|
|
55
|
+
apply={(prevDistanceFromBottom) => {
|
|
56
|
+
const el = scrollerRef.current!;
|
|
57
|
+
el.scrollTop = el.scrollHeight - prevDistanceFromBottom;
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<div ref={scrollerRef} style={{ height: 200, overflow: 'auto' }}>
|
|
61
|
+
{messages.map((msg, i) => (
|
|
62
|
+
<div key={i}>{msg}</div>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
</SnapshotBeforeUpdate>
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Without the wrapper, every prepend visually shifts the user's content down. With it, the visible content stays anchored.
|
|
72
|
+
|
|
73
|
+
## Capturing multiple values
|
|
74
|
+
|
|
75
|
+
`capture` returns a single value, but that value can be **any shape** — including an object. This is the recommended way to forward multiple pieces of state in one commit, and it keeps the API surface minimal and type-safe.
|
|
76
|
+
|
|
77
|
+
The example below combines two ideas at once:
|
|
78
|
+
|
|
79
|
+
1. Capture two DOM-derived values (`distanceFromBottom`, `wasAtBottom`) into one snapshot.
|
|
80
|
+
2. Reach into the surrounding component's state via **closure** (`messages.length`) so `apply` can compare "before" with "after" without a separate ref.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { useRef, useState } from 'react';
|
|
84
|
+
import { SnapshotBeforeUpdate } from 'react-snapshot-bridge';
|
|
85
|
+
|
|
86
|
+
type Snap = {
|
|
87
|
+
distanceFromBottom: number;
|
|
88
|
+
wasAtBottom: boolean;
|
|
89
|
+
prevCount: number;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function ChatList({ messages }: { messages: string[] }) {
|
|
93
|
+
const scrollerRef = useRef<HTMLDivElement>(null);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<SnapshotBeforeUpdate<Snap>
|
|
97
|
+
capture={() => {
|
|
98
|
+
const el = scrollerRef.current!;
|
|
99
|
+
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
100
|
+
return {
|
|
101
|
+
distanceFromBottom,
|
|
102
|
+
wasAtBottom: distanceFromBottom < 16,
|
|
103
|
+
// `messages` here is the value at this commit — by the time `apply`
|
|
104
|
+
// runs, the closure inside `apply` will see the new `messages`.
|
|
105
|
+
prevCount: messages.length,
|
|
106
|
+
};
|
|
107
|
+
}}
|
|
108
|
+
apply={({ distanceFromBottom, wasAtBottom, prevCount }) => {
|
|
109
|
+
const el = scrollerRef.current!;
|
|
110
|
+
const grewAtTop = messages.length > prevCount;
|
|
111
|
+
|
|
112
|
+
if (wasAtBottom) {
|
|
113
|
+
el.scrollTop = el.scrollHeight; // stick to bottom for new arrivals
|
|
114
|
+
} else if (grewAtTop) {
|
|
115
|
+
el.scrollTop = el.scrollHeight - distanceFromBottom; // anchor existing content
|
|
116
|
+
}
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
<div ref={scrollerRef} style={{ height: 200, overflow: 'auto' }}>
|
|
120
|
+
{messages.map((msg, i) => (
|
|
121
|
+
<div key={i}>{msg}</div>
|
|
122
|
+
))}
|
|
123
|
+
</div>
|
|
124
|
+
</SnapshotBeforeUpdate>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
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. Capturing what you actually need via closure is both safer and clearer.
|
|
130
|
+
|
|
131
|
+
## API
|
|
132
|
+
|
|
133
|
+
### `<SnapshotBeforeUpdate<T> capture apply>{children?}</SnapshotBeforeUpdate>`
|
|
134
|
+
|
|
135
|
+
| Prop | Type | Required | Description |
|
|
136
|
+
| ---------- | ----------------------------- | -------- | -------------------------------------------------------------------------------------------- |
|
|
137
|
+
| `capture` | `() => T` | yes | Called synchronously **before** DOM mutation. Return value is forwarded to `apply`. |
|
|
138
|
+
| `apply` | `(snapshot: T) => void` | yes | Called synchronously **after** DOM mutation, before paint. Receives the value from `capture`. |
|
|
139
|
+
| `children` | `React.ReactNode` | no | Optional. When provided, rendered as-is (wrapper pattern). When omitted, renders nothing. |
|
|
140
|
+
|
|
141
|
+
The generic `T` is inferred from `capture`'s return type — you usually don't need to specify it.
|
|
142
|
+
|
|
143
|
+
## How it works
|
|
144
|
+
|
|
145
|
+
React's commit phase runs in this order:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Render → Before Mutation → DOM Mutation → After Mutation → Paint → Passive
|
|
149
|
+
▲ ▲
|
|
150
|
+
│ │
|
|
151
|
+
getSnapshotBeforeUpdate cDU + useLayoutEffect
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Function components only have hooks for the **After Mutation** slot (`useLayoutEffect`). This library renders a tiny class component into your tree whose sole job is to occupy the **Before Mutation** slot and call your `capture` callback there.
|
|
155
|
+
|
|
156
|
+
The captured value is forwarded to `componentDidUpdate`, which calls `apply`. From your function component's point of view, you get a clean `(capture, apply)` pair without ever touching a class.
|
|
157
|
+
|
|
158
|
+
## Caveats
|
|
159
|
+
|
|
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
|
+
- **Requires a re-render.** If a `React.memo` (or `shouldComponentUpdate`) ancestor short-circuits the render, the bridge will not fire either. Make sure the component is in a tree that actually re-renders when the relevant change happens.
|
|
162
|
+
- **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
|
+
- **SSR safe.** Lifecycle methods don't run during `renderToString` / `renderToPipeableStream`, so the bridge is a no-op on the server.
|
|
164
|
+
- **React 19.** Class components and `getSnapshotBeforeUpdate` are not deprecated in React 19. This library remains compatible.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
[MIT](./LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
// src/SnapshotBeforeUpdate.tsx
|
|
6
|
+
var SnapshotBeforeUpdate = class extends react.Component {
|
|
7
|
+
getSnapshotBeforeUpdate() {
|
|
8
|
+
return this.props.capture();
|
|
9
|
+
}
|
|
10
|
+
componentDidUpdate(_prevProps, _prevState, snapshot) {
|
|
11
|
+
this.props.apply(snapshot);
|
|
12
|
+
}
|
|
13
|
+
render() {
|
|
14
|
+
return this.props.children ?? null;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
exports.SnapshotBeforeUpdate = SnapshotBeforeUpdate;
|
|
19
|
+
//# sourceMappingURL=index.cjs.map
|
|
20
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SnapshotBeforeUpdate.tsx"],"names":["Component"],"mappings":";;;;;AAkCO,IAAM,oBAAA,GAAN,cAAgDA,eAAA,CAGrD;AAAA,EACS,uBAAA,GAA6B;AACpC,IAAA,OAAO,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA,EAC5B;AAAA,EAES,kBAAA,CACP,UAAA,EACA,UAAA,EACA,QAAA,EACM;AACN,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\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 * 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 return this.props.capture();\n }\n\n override componentDidUpdate(\n _prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n _prevState: Readonly<EmptyState>,\n snapshot: T,\n ): void {\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
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Component, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
type EmptyState = Record<string, never>;
|
|
4
|
+
interface SnapshotBeforeUpdateProps<T> {
|
|
5
|
+
/**
|
|
6
|
+
* Runs synchronously **before** the DOM is mutated for this commit.
|
|
7
|
+
* Whatever you return here is forwarded to {@link SnapshotBeforeUpdateProps.apply}.
|
|
8
|
+
*/
|
|
9
|
+
capture: () => T;
|
|
10
|
+
/**
|
|
11
|
+
* Runs synchronously **after** the DOM is mutated, before the browser paints.
|
|
12
|
+
* Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.
|
|
13
|
+
*/
|
|
14
|
+
apply: (snapshot: T) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Optional children. When provided, this component acts as a wrapper and
|
|
17
|
+
* renders its children. When omitted, it renders nothing and you can place
|
|
18
|
+
* it as a sibling next to the elements you care about.
|
|
19
|
+
*/
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Bridges React's class-only `getSnapshotBeforeUpdate` lifecycle into trees
|
|
24
|
+
* built with function components.
|
|
25
|
+
*
|
|
26
|
+
* The component re-renders alongside its parent and uses the `getSnapshotBeforeUpdate`
|
|
27
|
+
* / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}
|
|
28
|
+
* before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.
|
|
29
|
+
*/
|
|
30
|
+
declare class SnapshotBeforeUpdate<T = unknown> extends Component<SnapshotBeforeUpdateProps<T>, EmptyState> {
|
|
31
|
+
getSnapshotBeforeUpdate(): T;
|
|
32
|
+
componentDidUpdate(_prevProps: Readonly<SnapshotBeforeUpdateProps<T>>, _prevState: Readonly<EmptyState>, snapshot: T): void;
|
|
33
|
+
render(): ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { SnapshotBeforeUpdate, type SnapshotBeforeUpdateProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Component, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
type EmptyState = Record<string, never>;
|
|
4
|
+
interface SnapshotBeforeUpdateProps<T> {
|
|
5
|
+
/**
|
|
6
|
+
* Runs synchronously **before** the DOM is mutated for this commit.
|
|
7
|
+
* Whatever you return here is forwarded to {@link SnapshotBeforeUpdateProps.apply}.
|
|
8
|
+
*/
|
|
9
|
+
capture: () => T;
|
|
10
|
+
/**
|
|
11
|
+
* Runs synchronously **after** the DOM is mutated, before the browser paints.
|
|
12
|
+
* Receives the value previously returned from {@link SnapshotBeforeUpdateProps.capture}.
|
|
13
|
+
*/
|
|
14
|
+
apply: (snapshot: T) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Optional children. When provided, this component acts as a wrapper and
|
|
17
|
+
* renders its children. When omitted, it renders nothing and you can place
|
|
18
|
+
* it as a sibling next to the elements you care about.
|
|
19
|
+
*/
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Bridges React's class-only `getSnapshotBeforeUpdate` lifecycle into trees
|
|
24
|
+
* built with function components.
|
|
25
|
+
*
|
|
26
|
+
* The component re-renders alongside its parent and uses the `getSnapshotBeforeUpdate`
|
|
27
|
+
* / `componentDidUpdate` pair internally to invoke {@link SnapshotBeforeUpdateProps.capture}
|
|
28
|
+
* before DOM mutation and {@link SnapshotBeforeUpdateProps.apply} immediately after.
|
|
29
|
+
*/
|
|
30
|
+
declare class SnapshotBeforeUpdate<T = unknown> extends Component<SnapshotBeforeUpdateProps<T>, EmptyState> {
|
|
31
|
+
getSnapshotBeforeUpdate(): T;
|
|
32
|
+
componentDidUpdate(_prevProps: Readonly<SnapshotBeforeUpdateProps<T>>, _prevState: Readonly<EmptyState>, snapshot: T): void;
|
|
33
|
+
render(): ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { SnapshotBeforeUpdate, type SnapshotBeforeUpdateProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/SnapshotBeforeUpdate.tsx
|
|
4
|
+
var SnapshotBeforeUpdate = class extends Component {
|
|
5
|
+
getSnapshotBeforeUpdate() {
|
|
6
|
+
return this.props.capture();
|
|
7
|
+
}
|
|
8
|
+
componentDidUpdate(_prevProps, _prevState, snapshot) {
|
|
9
|
+
this.props.apply(snapshot);
|
|
10
|
+
}
|
|
11
|
+
render() {
|
|
12
|
+
return this.props.children ?? null;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export { SnapshotBeforeUpdate };
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SnapshotBeforeUpdate.tsx"],"names":[],"mappings":";;;AAkCO,IAAM,oBAAA,GAAN,cAAgD,SAAA,CAGrD;AAAA,EACS,uBAAA,GAA6B;AACpC,IAAA,OAAO,IAAA,CAAK,MAAM,OAAA,EAAQ;AAAA,EAC5B;AAAA,EAES,kBAAA,CACP,UAAA,EACA,UAAA,EACA,QAAA,EACM;AACN,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\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 * 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 return this.props.capture();\n }\n\n override componentDidUpdate(\n _prevProps: Readonly<SnapshotBeforeUpdateProps<T>>,\n _prevState: Readonly<EmptyState>,\n snapshot: T,\n ): void {\n this.props.apply(snapshot);\n }\n\n override render(): ReactNode {\n return this.props.children ?? null;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-snapshot-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Alternative GetSnapshotBeforeUpdate Hook for React Functional Components",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"getSnapshotBeforeUpdate",
|
|
8
|
+
"lifecycle",
|
|
9
|
+
"snapshot",
|
|
10
|
+
"hooks",
|
|
11
|
+
"useLayoutEffect",
|
|
12
|
+
"before-mutation"
|
|
13
|
+
],
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "ksr20612",
|
|
16
|
+
"url": "https://github.com/ksr20612"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/ksr20612/react-snapshot-bridge.git",
|
|
22
|
+
"directory": "packages/react-snapshot-bridge"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/ksr20612/react-snapshot-bridge#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/ksr20612/react-snapshot-bridge/issues"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"main": "./dist/index.cjs",
|
|
30
|
+
"module": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/index.d.cts",
|
|
40
|
+
"default": "./dist/index.cjs"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
45
|
+
"sideEffects": false,
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"registry": "https://registry.npmjs.org/"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20"
|
|
52
|
+
},
|
|
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
|
+
"peerDependencies": {
|
|
62
|
+
"react": ">=16.3.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@testing-library/react": "^16.1.0",
|
|
66
|
+
"@types/react": "^18.3.12",
|
|
67
|
+
"@types/react-dom": "^18.3.1",
|
|
68
|
+
"jsdom": "^25.0.1",
|
|
69
|
+
"react": "^18.3.1",
|
|
70
|
+
"react-dom": "^18.3.1",
|
|
71
|
+
"tsup": "^8.3.5",
|
|
72
|
+
"typescript": "^5.7.2",
|
|
73
|
+
"vitest": "^2.1.8"
|
|
74
|
+
}
|
|
75
|
+
}
|