Skip to content

Commit d502a69

Browse files
authored
useMutation: fix rules of React violations (#11852)
* useMutation: fix rules of React violations * size-limits * Clean up Prettier, Size-limit, and Api-Extractor --------- Co-authored-by: phryneas <phryneas@users.noreply.github.com>
1 parent 45c47be commit d502a69

File tree

6 files changed

+27
-11
lines changed

6 files changed

+27
-11
lines changed

.changeset/loud-hairs-think.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Fix a bug where calling the `useMutation` `reset` function would point the hook to an outdated `client` reference.

.changeset/mighty-monkeys-explain.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Prevent writing to a ref in render in `useMutation`.
6+
As a result, you might encounter problems in the future if you call the mutation's `execute` function during render. Please note that this was never supported behavior, and we strongly recommend against it.

.size-limits.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"dist/apollo-client.min.cjs": 39607,
2+
"dist/apollo-client.min.cjs": 39620,
33
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32821
44
}

src/react/components/__tests__/client/Mutation.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,7 @@ describe("General Mutation testing", () => {
13481348
if (count === 0) {
13491349
expect(result.called).toEqual(false);
13501350
expect(result.loading).toEqual(false);
1351-
createTodo();
1351+
setTimeout(createTodo, 10);
13521352
} else if (count === 2 && result) {
13531353
expect(result.data).toEqual(data);
13541354
setTimeout(() => {
@@ -1358,7 +1358,7 @@ describe("General Mutation testing", () => {
13581358
});
13591359
} else if (count === 3) {
13601360
expect(result.loading).toEqual(false);
1361-
createTodo();
1361+
setTimeout(createTodo, 10);
13621362
} else if (count === 5) {
13631363
expect(result.data).toEqual(data3);
13641364
}

src/react/hoc/__tests__/mutations/lifecycle.test.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ describe("graphql(mutation) lifecycle", () => {
9292
class Container extends React.Component<ChildProps<Props>> {
9393
render() {
9494
if (this.props.listId !== 2) return null;
95-
this.props.mutate!().then(() => resolve());
95+
setTimeout(() => {
96+
this.props.mutate!().then(() => resolve());
97+
});
9698
return null;
9799
}
98100
}

src/react/hooks/useMutation.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,9 @@ export function useMutation<
9999
options,
100100
});
101101

102-
// TODO: Trying to assign these in a useEffect or useLayoutEffect breaks
103-
// higher-order components.
104-
{
102+
React.useLayoutEffect(() => {
105103
Object.assign(ref.current, { client, options, mutation });
106-
}
104+
});
107105

108106
const execute = React.useCallback(
109107
(
@@ -221,17 +219,22 @@ export function useMutation<
221219

222220
const reset = React.useCallback(() => {
223221
if (ref.current.isMounted) {
224-
const result = { called: false, loading: false, client };
222+
const result = {
223+
called: false,
224+
loading: false,
225+
client: ref.current.client,
226+
};
225227
Object.assign(ref.current, { mutationId: 0, result });
226228
setResult(result);
227229
}
228230
}, []);
229231

230232
React.useEffect(() => {
231-
ref.current.isMounted = true;
233+
const current = ref.current;
234+
current.isMounted = true;
232235

233236
return () => {
234-
ref.current.isMounted = false;
237+
current.isMounted = false;
235238
};
236239
}, []);
237240

0 commit comments

Comments
 (0)