atomirx 0.0.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 +1666 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +1440 -0
- package/coverage/coverage-final.json +14 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/core/atom.ts.html +889 -0
- package/coverage/src/core/batch.ts.html +223 -0
- package/coverage/src/core/define.ts.html +805 -0
- package/coverage/src/core/emitter.ts.html +919 -0
- package/coverage/src/core/equality.ts.html +631 -0
- package/coverage/src/core/hook.ts.html +460 -0
- package/coverage/src/core/index.html +281 -0
- package/coverage/src/core/isAtom.ts.html +100 -0
- package/coverage/src/core/isPromiseLike.ts.html +133 -0
- package/coverage/src/core/onCreateHook.ts.html +136 -0
- package/coverage/src/core/scheduleNotifyHook.ts.html +94 -0
- package/coverage/src/core/types.ts.html +523 -0
- package/coverage/src/core/withUse.ts.html +253 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/index.ts.html +106 -0
- package/dist/core/atom.d.ts +63 -0
- package/dist/core/atom.test.d.ts +1 -0
- package/dist/core/atomState.d.ts +104 -0
- package/dist/core/atomState.test.d.ts +1 -0
- package/dist/core/batch.d.ts +126 -0
- package/dist/core/batch.test.d.ts +1 -0
- package/dist/core/define.d.ts +173 -0
- package/dist/core/define.test.d.ts +1 -0
- package/dist/core/derived.d.ts +102 -0
- package/dist/core/derived.test.d.ts +1 -0
- package/dist/core/effect.d.ts +120 -0
- package/dist/core/effect.test.d.ts +1 -0
- package/dist/core/emitter.d.ts +237 -0
- package/dist/core/emitter.test.d.ts +1 -0
- package/dist/core/equality.d.ts +62 -0
- package/dist/core/equality.test.d.ts +1 -0
- package/dist/core/hook.d.ts +134 -0
- package/dist/core/hook.test.d.ts +1 -0
- package/dist/core/isAtom.d.ts +9 -0
- package/dist/core/isPromiseLike.d.ts +9 -0
- package/dist/core/isPromiseLike.test.d.ts +1 -0
- package/dist/core/onCreateHook.d.ts +79 -0
- package/dist/core/promiseCache.d.ts +134 -0
- package/dist/core/promiseCache.test.d.ts +1 -0
- package/dist/core/scheduleNotifyHook.d.ts +51 -0
- package/dist/core/select.d.ts +151 -0
- package/dist/core/selector.test.d.ts +1 -0
- package/dist/core/types.d.ts +279 -0
- package/dist/core/withUse.d.ts +38 -0
- package/dist/core/withUse.test.d.ts +1 -0
- package/dist/index-2ok7ilik.js +1217 -0
- package/dist/index-B_5SFzfl.cjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +20 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/react/index.cjs +30 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +823 -0
- package/dist/react/rx.d.ts +250 -0
- package/dist/react/rx.test.d.ts +1 -0
- package/dist/react/strictModeTest.d.ts +10 -0
- package/dist/react/useAction.d.ts +381 -0
- package/dist/react/useAction.test.d.ts +1 -0
- package/dist/react/useStable.d.ts +183 -0
- package/dist/react/useStable.test.d.ts +1 -0
- package/dist/react/useValue.d.ts +134 -0
- package/dist/react/useValue.test.d.ts +1 -0
- package/package.json +57 -0
- package/scripts/publish.js +198 -0
- package/src/core/atom.test.ts +369 -0
- package/src/core/atom.ts +189 -0
- package/src/core/atomState.test.ts +342 -0
- package/src/core/atomState.ts +256 -0
- package/src/core/batch.test.ts +257 -0
- package/src/core/batch.ts +172 -0
- package/src/core/define.test.ts +342 -0
- package/src/core/define.ts +243 -0
- package/src/core/derived.test.ts +381 -0
- package/src/core/derived.ts +339 -0
- package/src/core/effect.test.ts +196 -0
- package/src/core/effect.ts +184 -0
- package/src/core/emitter.test.ts +364 -0
- package/src/core/emitter.ts +392 -0
- package/src/core/equality.test.ts +392 -0
- package/src/core/equality.ts +182 -0
- package/src/core/hook.test.ts +227 -0
- package/src/core/hook.ts +177 -0
- package/src/core/isAtom.ts +27 -0
- package/src/core/isPromiseLike.test.ts +72 -0
- package/src/core/isPromiseLike.ts +16 -0
- package/src/core/onCreateHook.ts +92 -0
- package/src/core/promiseCache.test.ts +239 -0
- package/src/core/promiseCache.ts +279 -0
- package/src/core/scheduleNotifyHook.ts +53 -0
- package/src/core/select.ts +454 -0
- package/src/core/selector.test.ts +257 -0
- package/src/core/types.ts +311 -0
- package/src/core/withUse.test.ts +249 -0
- package/src/core/withUse.ts +56 -0
- package/src/index.test.ts +80 -0
- package/src/index.ts +51 -0
- package/src/react/index.ts +20 -0
- package/src/react/rx.test.tsx +416 -0
- package/src/react/rx.tsx +300 -0
- package/src/react/strictModeTest.tsx +71 -0
- package/src/react/useAction.test.ts +989 -0
- package/src/react/useAction.ts +605 -0
- package/src/react/useStable.test.ts +553 -0
- package/src/react/useStable.ts +288 -0
- package/src/react/useValue.test.ts +182 -0
- package/src/react/useValue.ts +261 -0
- package/tsconfig.json +9 -0
- package/v2.md +725 -0
- package/vite.config.ts +39 -0
package/src/react/rx.tsx
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { memo } from "react";
|
|
2
|
+
import { Atom, Equality } from "../core/types";
|
|
3
|
+
import { useValue } from "./useValue";
|
|
4
|
+
import { shallowEqual } from "../core/equality";
|
|
5
|
+
import { isAtom } from "../core/isAtom";
|
|
6
|
+
import { ContextSelectorFn } from "../core/select";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reactive inline component that renders atom values directly in JSX.
|
|
10
|
+
*
|
|
11
|
+
* `rx` is a convenience wrapper around `useValue` that returns a memoized
|
|
12
|
+
* React component instead of a value. This enables fine-grained reactivity
|
|
13
|
+
* without creating separate components for each reactive value.
|
|
14
|
+
*
|
|
15
|
+
* ## IMPORTANT: Selector Must Return Synchronous Value
|
|
16
|
+
*
|
|
17
|
+
* **The selector function MUST NOT be async or return a Promise.**
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* // ❌ WRONG - Don't use async function
|
|
21
|
+
* rx(async ({ get }) => {
|
|
22
|
+
* const data = await fetch('/api');
|
|
23
|
+
* return data.name;
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // ❌ WRONG - Don't return a Promise
|
|
27
|
+
* rx(({ get }) => fetch('/api').then(r => r.json()));
|
|
28
|
+
*
|
|
29
|
+
* // ✅ CORRECT - Create async atom and read with get()
|
|
30
|
+
* const data$ = atom(fetch('/api').then(r => r.json()));
|
|
31
|
+
* rx(({ get }) => get(data$).name); // Suspends until resolved
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ## Why Use `rx`?
|
|
35
|
+
*
|
|
36
|
+
* Without `rx`, you need a separate component to subscribe to an atom:
|
|
37
|
+
* ```tsx
|
|
38
|
+
* function PostsList() {
|
|
39
|
+
* const posts = useValue(postsAtom);
|
|
40
|
+
* return posts.map((post) => <Post post={post} />);
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* function Page() {
|
|
44
|
+
* return (
|
|
45
|
+
* <Suspense fallback={<Loading />}>
|
|
46
|
+
* <PostsList />
|
|
47
|
+
* </Suspense>
|
|
48
|
+
* );
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* With `rx`, you can subscribe inline:
|
|
53
|
+
* ```tsx
|
|
54
|
+
* function Page() {
|
|
55
|
+
* return (
|
|
56
|
+
* <Suspense fallback={<Loading />}>
|
|
57
|
+
* {rx(({ get }) =>
|
|
58
|
+
* get(postsAtom).map((post) => <Post post={post} />)
|
|
59
|
+
* )}
|
|
60
|
+
* </Suspense>
|
|
61
|
+
* );
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* ## Key Benefits
|
|
66
|
+
*
|
|
67
|
+
* 1. **Fine-grained updates**: Only the `rx` component re-renders when the atom changes,
|
|
68
|
+
* not the parent component
|
|
69
|
+
* 2. **Less boilerplate**: No need to create single-purpose wrapper components
|
|
70
|
+
* 3. **Colocation**: Keep reactive logic inline where it's used
|
|
71
|
+
* 4. **Memoized**: Uses `React.memo` to prevent unnecessary re-renders
|
|
72
|
+
* 5. **Type-safe**: Full TypeScript support with proper type inference
|
|
73
|
+
*
|
|
74
|
+
* ## Async Atoms (Suspense-Style API)
|
|
75
|
+
*
|
|
76
|
+
* `rx` inherits the Suspense-style API from `useValue`:
|
|
77
|
+
* - **Loading state**: The getter throws a Promise (triggers Suspense)
|
|
78
|
+
* - **Error state**: The getter throws the error (triggers ErrorBoundary)
|
|
79
|
+
* - **Resolved state**: The getter returns the value
|
|
80
|
+
*
|
|
81
|
+
* For async atoms, you MUST wrap with `<Suspense>` and `<ErrorBoundary>`:
|
|
82
|
+
* ```tsx
|
|
83
|
+
* function App() {
|
|
84
|
+
* return (
|
|
85
|
+
* <ErrorBoundary fallback={<div>Error!</div>}>
|
|
86
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
87
|
+
* {rx(({ get }) => get(userAtom).name)}
|
|
88
|
+
* </Suspense>
|
|
89
|
+
* </ErrorBoundary>
|
|
90
|
+
* );
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* Or catch errors in the selector to handle loading/error inline:
|
|
95
|
+
* ```tsx
|
|
96
|
+
* {rx(({ get }) => {
|
|
97
|
+
* try {
|
|
98
|
+
* return get(userAtom).name;
|
|
99
|
+
* } catch {
|
|
100
|
+
* return "Loading...";
|
|
101
|
+
* }
|
|
102
|
+
* })}
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @template T - The type of the selected/derived value
|
|
106
|
+
* @param selector - Context-based selector function with `{ get, all, any, race, settled }`.
|
|
107
|
+
* Must return sync value, not a Promise.
|
|
108
|
+
* @param equals - Equality function or shorthand ("strict", "shallow", "deep").
|
|
109
|
+
* Defaults to "shallow".
|
|
110
|
+
* @returns A React element that renders the selected value
|
|
111
|
+
* @throws Error if selector returns a Promise or PromiseLike
|
|
112
|
+
*
|
|
113
|
+
* @example Shorthand - render atom value directly
|
|
114
|
+
* ```tsx
|
|
115
|
+
* const count = atom(5);
|
|
116
|
+
*
|
|
117
|
+
* function Counter() {
|
|
118
|
+
* return <div>Count: {rx(count)}</div>;
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example Context selector - derive a value
|
|
123
|
+
* ```tsx
|
|
124
|
+
* const count = atom(5);
|
|
125
|
+
*
|
|
126
|
+
* function DoubledCounter() {
|
|
127
|
+
* return <div>Doubled: {rx(({ get }) => get(count) * 2)}</div>;
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @example Multiple atoms
|
|
132
|
+
* ```tsx
|
|
133
|
+
* const firstName = atom("John");
|
|
134
|
+
* const lastName = atom("Doe");
|
|
135
|
+
*
|
|
136
|
+
* function FullName() {
|
|
137
|
+
* return (
|
|
138
|
+
* <div>
|
|
139
|
+
* {rx(({ get }) => `${get(firstName)} ${get(lastName)}`)}
|
|
140
|
+
* </div>
|
|
141
|
+
* );
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @example Fine-grained updates - parent doesn't re-render
|
|
146
|
+
* ```tsx
|
|
147
|
+
* const count = atom(0);
|
|
148
|
+
*
|
|
149
|
+
* function Parent() {
|
|
150
|
+
* console.log("Parent renders once");
|
|
151
|
+
* return (
|
|
152
|
+
* <div>
|
|
153
|
+
* {rx(count)} {/* Only this re-renders when count changes *\/}
|
|
154
|
+
* <button onClick={() => count.set((n) => n + 1)}>+</button>
|
|
155
|
+
* </div>
|
|
156
|
+
* );
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example Multiple subscriptions in one component
|
|
161
|
+
* ```tsx
|
|
162
|
+
* function Dashboard() {
|
|
163
|
+
* return (
|
|
164
|
+
* <div>
|
|
165
|
+
* <header>
|
|
166
|
+
* <Suspense fallback="...">{rx(({ get }) => get(userAtom).name)}</Suspense>
|
|
167
|
+
* </header>
|
|
168
|
+
* <main>
|
|
169
|
+
* <Suspense fallback="...">
|
|
170
|
+
* {rx(({ get }) => get(postsAtom).length)} posts
|
|
171
|
+
* </Suspense>
|
|
172
|
+
* <Suspense fallback="...">
|
|
173
|
+
* {rx(({ get }) => get(notificationsAtom).length)} notifications
|
|
174
|
+
* </Suspense>
|
|
175
|
+
* </main>
|
|
176
|
+
* </div>
|
|
177
|
+
* );
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* @example Conditional dependencies - only subscribes to accessed atoms
|
|
182
|
+
* ```tsx
|
|
183
|
+
* const showDetails = atom(false);
|
|
184
|
+
* const summary = atom("Brief info");
|
|
185
|
+
* const details = atom("Detailed info");
|
|
186
|
+
*
|
|
187
|
+
* function Info() {
|
|
188
|
+
* return (
|
|
189
|
+
* <div>
|
|
190
|
+
* {rx(({ get }) =>
|
|
191
|
+
* get(showDetails) ? get(details) : get(summary)
|
|
192
|
+
* )}
|
|
193
|
+
* </div>
|
|
194
|
+
* );
|
|
195
|
+
* }
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* @example With custom equality
|
|
199
|
+
* ```tsx
|
|
200
|
+
* const user = atom({ id: 1, name: "John" });
|
|
201
|
+
*
|
|
202
|
+
* function UserName() {
|
|
203
|
+
* return (
|
|
204
|
+
* <div>
|
|
205
|
+
* {rx(
|
|
206
|
+
* ({ get }) => get(user).name,
|
|
207
|
+
* (a, b) => a === b // Only re-render if name string changes
|
|
208
|
+
* )}
|
|
209
|
+
* </div>
|
|
210
|
+
* );
|
|
211
|
+
* }
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* @example Combining multiple async atoms with async utilities
|
|
215
|
+
* ```tsx
|
|
216
|
+
* const userAtom = atom(fetchUser());
|
|
217
|
+
* const postsAtom = atom(fetchPosts());
|
|
218
|
+
*
|
|
219
|
+
* function Dashboard() {
|
|
220
|
+
* return (
|
|
221
|
+
* <Suspense fallback={<Loading />}>
|
|
222
|
+
* {rx(({ all }) => {
|
|
223
|
+
* // Use all() to wait for multiple atoms
|
|
224
|
+
* const [user, posts] = all([userAtom, postsAtom]);
|
|
225
|
+
* return <DashboardContent user={user} posts={posts} />;
|
|
226
|
+
* })}
|
|
227
|
+
* </Suspense>
|
|
228
|
+
* );
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*
|
|
232
|
+
* @example Using settled for partial failures
|
|
233
|
+
* ```tsx
|
|
234
|
+
* const userAtom = atom(fetchUser());
|
|
235
|
+
* const postsAtom = atom(fetchPosts());
|
|
236
|
+
*
|
|
237
|
+
* function Dashboard() {
|
|
238
|
+
* return (
|
|
239
|
+
* <Suspense fallback={<Loading />}>
|
|
240
|
+
* {rx(({ settled }) => {
|
|
241
|
+
* const [userResult, postsResult] = settled([userAtom, postsAtom]);
|
|
242
|
+
* return (
|
|
243
|
+
* <DashboardContent
|
|
244
|
+
* user={userResult.status === 'resolved' ? userResult.value : null}
|
|
245
|
+
* posts={postsResult.status === 'resolved' ? postsResult.value : []}
|
|
246
|
+
* />
|
|
247
|
+
* );
|
|
248
|
+
* })}
|
|
249
|
+
* </Suspense>
|
|
250
|
+
* );
|
|
251
|
+
* }
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
// Overload: Pass atom directly to get its value (shorthand)
|
|
255
|
+
export function rx<T>(atom: Atom<T>, equals?: Equality<Awaited<T>>): Awaited<T>;
|
|
256
|
+
|
|
257
|
+
// Overload: Context-based selector function
|
|
258
|
+
export function rx<T>(selector: ContextSelectorFn<T>, equals?: Equality<T>): T;
|
|
259
|
+
|
|
260
|
+
export function rx<T>(
|
|
261
|
+
selectorOrAtom: ContextSelectorFn<T> | Atom<T>,
|
|
262
|
+
equals?: Equality<unknown>
|
|
263
|
+
): T {
|
|
264
|
+
return (
|
|
265
|
+
<Rx
|
|
266
|
+
selectorOrAtom={
|
|
267
|
+
selectorOrAtom as ContextSelectorFn<unknown> | Atom<unknown>
|
|
268
|
+
}
|
|
269
|
+
equals={equals}
|
|
270
|
+
/>
|
|
271
|
+
) as unknown as T;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Internal memoized component that handles the actual subscription and rendering.
|
|
276
|
+
*
|
|
277
|
+
* Memoized with React.memo to ensure:
|
|
278
|
+
* 1. Parent components don't cause unnecessary re-renders
|
|
279
|
+
* 2. Only atom changes trigger re-renders
|
|
280
|
+
* 3. Props comparison is shallow (selectorOrAtom, equals references)
|
|
281
|
+
*
|
|
282
|
+
* Renders `selected ?? null` to handle null/undefined values gracefully in JSX.
|
|
283
|
+
*/
|
|
284
|
+
const Rx = memo(
|
|
285
|
+
function Rx(props: {
|
|
286
|
+
selectorOrAtom: ContextSelectorFn<unknown> | Atom<unknown>;
|
|
287
|
+
equals?: Equality<unknown>;
|
|
288
|
+
}) {
|
|
289
|
+
// Convert atom shorthand to context selector
|
|
290
|
+
const selector: ContextSelectorFn<unknown> = isAtom(props.selectorOrAtom)
|
|
291
|
+
? ({ get }) => get(props.selectorOrAtom as Atom<unknown>)
|
|
292
|
+
: (props.selectorOrAtom as ContextSelectorFn<unknown>);
|
|
293
|
+
|
|
294
|
+
const selected = useValue(selector, props.equals);
|
|
295
|
+
return <>{selected ?? null}</>;
|
|
296
|
+
},
|
|
297
|
+
(prev, next) =>
|
|
298
|
+
shallowEqual(prev.selectorOrAtom, next.selectorOrAtom) &&
|
|
299
|
+
prev.equals === next.equals
|
|
300
|
+
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PropsWithChildren, StrictMode } from "react";
|
|
3
|
+
import { render, renderHook, RenderHookOptions } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
const DefaultWrapper = ({ children }: PropsWithChildren) => <>{children}</>;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Composes two React wrapper components.
|
|
9
|
+
* OuterWrapper wraps InnerWrapper which wraps children.
|
|
10
|
+
*/
|
|
11
|
+
function composeWrappers(
|
|
12
|
+
OuterWrapper: React.ComponentType<{ children: React.ReactNode }>,
|
|
13
|
+
InnerWrapper?: React.ComponentType<{ children: React.ReactNode }>
|
|
14
|
+
): React.ComponentType<{ children: React.ReactNode }> {
|
|
15
|
+
if (!InnerWrapper) {
|
|
16
|
+
return OuterWrapper;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return function ComposedWrapper({ children }: { children: React.ReactNode }) {
|
|
20
|
+
return (
|
|
21
|
+
<OuterWrapper>
|
|
22
|
+
<InnerWrapper>{children}</InnerWrapper>
|
|
23
|
+
</OuterWrapper>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const StrictModeWrapper = ({ children }: PropsWithChildren) => (
|
|
29
|
+
<StrictMode>{children}</StrictMode>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const wrappers: {
|
|
33
|
+
mode: "normal" | "strict";
|
|
34
|
+
Wrapper: React.FC<{ children: React.ReactNode }>;
|
|
35
|
+
render: (ui: React.ReactElement) => ReturnType<typeof render>;
|
|
36
|
+
renderHook: <TResult, TProps>(
|
|
37
|
+
render: (props: TProps) => TResult,
|
|
38
|
+
options?: RenderHookOptions<TProps>
|
|
39
|
+
) => ReturnType<typeof renderHook<TResult, TProps>>;
|
|
40
|
+
}[] = [
|
|
41
|
+
{
|
|
42
|
+
mode: "normal" as const,
|
|
43
|
+
Wrapper: DefaultWrapper,
|
|
44
|
+
render: (ui: React.ReactElement) => {
|
|
45
|
+
return render(ui, { wrapper: DefaultWrapper });
|
|
46
|
+
},
|
|
47
|
+
renderHook: <TResult, TProps>(
|
|
48
|
+
callback: (props: TProps) => TResult,
|
|
49
|
+
options?: RenderHookOptions<TProps>
|
|
50
|
+
) => {
|
|
51
|
+
return renderHook(callback, options);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
mode: "strict" as const,
|
|
56
|
+
Wrapper: StrictModeWrapper,
|
|
57
|
+
render: (ui: React.ReactElement) => {
|
|
58
|
+
return render(ui, { wrapper: StrictModeWrapper });
|
|
59
|
+
},
|
|
60
|
+
renderHook: <TResult, TProps>(
|
|
61
|
+
callback: (props: TProps) => TResult,
|
|
62
|
+
options?: RenderHookOptions<TProps>
|
|
63
|
+
) => {
|
|
64
|
+
const composedWrapper = composeWrappers(
|
|
65
|
+
StrictModeWrapper,
|
|
66
|
+
options?.wrapper
|
|
67
|
+
);
|
|
68
|
+
return renderHook(callback, { ...options, wrapper: composedWrapper });
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
];
|