Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5ba7fe9

Browse files
committed
WIP: migration guide
1 parent a267f1e commit 5ba7fe9

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

‎MIGRATION_GUIDE.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# React 18 Migration guide
2+
3+
React Hooks Testing Library will not be updated to support React 18. Instead, React Testing Library
4+
and React Native Testing Library are including their own `renderHook` APIs with the goal of
5+
providing more unified and consistent experience for our users.
6+
7+
In general, the new `renderHook` functions are largely compatible with the React Hooks Testing
8+
Library version and many users will just be able to update their imports, but there are a few
9+
notable exceptions as well as some scenarios which are no longer supported at all. This guide will
10+
outline what has changed and what has been dropped and some strategies to smooth the transition.
11+
12+
## Choose your renderer
13+
14+
React Hooks Testing Library supported three different React renderers that could be used for testing
15+
hooks in different types of environments, as well as a auto-detect import that would attempt to
16+
resolve whichever renderer happened to be installed. `renderHook` could be imported using any of the
17+
following modules:
18+
19+
1. `@testing-library/react-hooks/dom` (`react-dom`), for testing hooks in a web environment
20+
2. `@testing-library/react-hooks/native` (`react-test-renderer`), for testing hooks in a
21+
`react-native` environment
22+
3. `@testing-library/react-hooks/server` (`react-dom/server`), for testing hooks in a SSR
23+
environment
24+
4. `@testing-library/react-hooks`, auto-detect either the `dom` or `native` variants based on the
25+
installed renderer
26+
27+
Depending on which renderer you were using will determine which package to migrate to.
28+
29+
- `@testing-library/react-hooks/dom`
30+
31+
```sh
32+
npm uninstall @testing-library/react-hooks
33+
npm install --save-dev @testing-library/react
34+
```
35+
36+
```diff
37+
-import { renderHook } from '@testing-library/react-hooks`;
38+
+import { renderHook } from '@testing-library/react';
39+
```
40+
41+
- `@testing-library/react-hooks/native`
42+
43+
```sh
44+
npm uninstall @testing-library/react-hooks
45+
npm install --save-dev @testing-library/react-native
46+
```
47+
48+
```diff
49+
-import { renderHook } from '@testing-library/react-hooks`;
50+
+import { renderHook } from '@testing-library/react-native';
51+
```
52+
53+
- `@testing-library/react-hooks/server`
54+
55+
> There is not an equivalent renderer for this import in the `@testing-library/react` package. You
56+
> will need to wrap the hook in your own test component and render it with `react-dom/server`
57+
> manually.
58+
59+
- `@testing-library/react-hooks`
60+
> If your project is a `react-native` app, follow the instructions above for
61+
> `@testing-library/react-hooks/native`, otherwise follow the instructions for
62+
> `@testing-library/react-hooks/dom`.
63+
64+
## `waitFor`
65+
66+
This utility should now be imported at the same time as `renderHook` instead of being accessed from
67+
the `renderHook` return value.
68+
69+
```diff
70+
-import { renderHook } from '@testing-library/react-hooks`;
71+
+import { renderHook, waitFor } from '@testing-library/react';
72+
+// or import { renderHook, waitFor } from '@testing-library/react-native';
73+
```
74+
75+
```diff
76+
-const { result, waitFor } = renderHook(() => useHook());
77+
+const { result } = renderHook(() => useHook());
78+
```
79+
80+
The React Hooks Testing Library version of `waitFor` supported either returning a `boolean` value or
81+
using assertions (e.g. `expect`) to wait for the condition to be met. Both the React Testing Library
82+
and React Native Testing Library version only support the assertion style for their `waitFor`
83+
utilities. If you were using the `boolean` style, you will need to update the callbacks like so:
84+
85+
```diff
86+
-await waitFor(() => result.current.state !== 'loading');
87+
+await waitFor(() => {
88+
+ expect(result.current.state).not.toBe('loading');
89+
+});
90+
```
91+
92+
It should also be noted that the React Hooks Testing Library version of `waitFor` would recheck the
93+
condition any time the hook triggered a render, as well as on a periodic interval but due to
94+
implementation differences of `waitFor` in the new version, the condition will only be checked on
95+
the interval. If your condition can potentially be missed by waiting for the default interval time
96+
(100ms), you may need to adjust the timings using the `interval` option:
97+
98+
```diff
99+
await waitFor(() => {
100+
expect(result.current.state).not.toBe('loading');
101+
-});
102+
+}, { interval: 20 });
103+
```
104+
105+
## `waitForValueToChange`
106+
107+
This utility has not been included in the React Testing Library or React Native Testing Library
108+
APIs. A similar result can be achieved by using `waitFor`:
109+
110+
```diff
111+
-await waitForValueToChange(() => result.current.state);
112+
+const initialValue = result.current.state;
113+
+await waitFor(() => {
114+
+ expect(result.current.state).not.toBe(initialValue);
115+
+});
116+
```
117+
118+
## `waitForNextUpdate`
119+
120+
This utility has not been included in the React Testing Library or React Native Testing Library
121+
APIs. A similar result can be achieved by using `waitFor`:
122+
123+
```diff
124+
-await waitForValueToChange(() => result.current.state);
125+
+const initialValue = result.current;
126+
+await waitFor(() => {
127+
+ expect(result.current).not.toBe(initialValue);
128+
+});
129+
```
130+
131+
Note that this is not quite the same as the previous implementation, which simply waited for the
132+
next render regardless of whether the value of `result.current` has changed or not, but this is more
133+
in line with how the utility was intended to be used. Writing tests that rely on specific timing or
134+
numbers of renders is discouraged in the Testing Library methodology as it focuses too much on
135+
implementation details of the hooks.
136+
137+
## `result.error`
138+
139+
Errors are now thrown directly from `renderHook`, `rerender` and `unmount` calls. If you were
140+
previously using `result.error` to test for error values, you should update your tests to instead
141+
check for thrown errors:
142+
143+
```diff
144+
-const { result } = renderHook(() => useHook());
145+
-expect(result.error).toBe(Error('something expected'));
146+
+expect(() => renderHook(() => useHook())).toThrow(Error('something expected'));
147+
```
148+
149+
There is an edge case that is no longer covered which is when an asynchronous update to a hook
150+
causes the next render to throw an error, e.g.
151+
152+
```ts
153+
function useAsyncValue() {
154+
const [loading, setLoading] = useState(true)
155+
const [value, setValue] = useState(null)
156+
const [error, setError] = useState(null)
157+
158+
useEffect(() => {
159+
getAsyncValue()
160+
.then(setValue)
161+
.catch(setError)
162+
.finally(() => setLoading(false))
163+
}, [])
164+
165+
if (error) {
166+
throw error
167+
}
168+
169+
return { loading, value }
170+
}
171+
```
172+
173+
In this scenario, calling `renderHook(() => useAsyncValue())` will not throw any errors. Tests that
174+
need to access an asynchronous error like this can use the `wrapper` option to wrap the hook call in
175+
an error boundary and capture the error there instead:
176+
177+
```diff
178+
+let asyncError = null;
179+
+
180+
+class ErrorBoundary extends React.Component {
181+
+ componentDidCatch(error) {
182+
+ asyncError = error;
183+
+ }
184+
+
185+
+ render() {
186+
+ return !asyncError && this.props.children;
187+
+ }
188+
+ }
189+
190+
-const { result, waitFor } = renderHook(() => useAsyncValue());
191+
+const { result } = renderHook(() => useAsyncValue(), {
192+
+ wrapper: ErrorBoundary,
193+
+});
194+
195+
await waitFor(() => {
196+
- expect(result.error).toEqual(Error('something expected'));
197+
+ expect(asyncError).toEqual(Error('something expected'));
198+
});
199+
```
200+
201+
## `result.all`
202+
203+
The new `renderHook` APIs in React Testing Library and React Native Testing Library have not
204+
included `result.all` as it was deemed to promote testing implementation details. Tests that rely on
205+
`result.all` should be rewritten to just use `result.current` and/or `waitFor` with more emphasis on
206+
testing the value that will be observed by users and not the intermediate values in between
207+
observable results.
208+
209+
## Suspense
210+
211+
Previously, React Hooks Testing Library would automatically wrap the hook call in a `Suspense`
212+
boundary. This functionality has not been replicated in either React Testing Library or React Native
213+
Testing Library so hooks that rely on suspense will need to add their own suspense boundaries using
214+
the `wrapper` option:
215+
216+
```diff
217+
+const SuspenseBoundary = ({ children }) => <Suspense fallback={null}>{children}</Suspense>
218+
219+
-const { result } = renderHook(() => useSuspendingHook());
220+
+const { result } = renderHook(() => useSuspendingHook(), {
221+
+ wrapper: SuspenseBoundary,
222+
+});
223+
```
224+
225+
## `wrapper` Props

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /