<MockedProvider /> Infinite Render with UseSubscription and onSubscriptionData
See original GitHub issueIntended outcome:
Intend to trigger one call of onSubscriptionData
for a component utilizing a subscription to fetch via useLazyQuery
.
Actual outcome: An infinite loop of re-renders results in a test that never completes.
How to reproduce the issue:
Example project can be found here. Run npm i
and npm run test
to reproduce.
The basics of the code are:
// component.tsx
import React from "react";
import { useLazyQuery, useSubscription } from "@apollo/client";
import { GET_USER } from "./queries";
import { SUBSCRIBE_TO_PURCHASE_UPDATES } from "./subscriptions";
type Purchase = { id: string; price: number };
type User = { id: string; purchases: Purchase[] };
type TheComponentProps = { user: User };
export const TheComponent = ({ user: propsUser }: TheComponentProps) => {
const [user, setUser] = React.useState(propsUser);
const userId = user.id;
const [getUser, { data: latestUserData }] = useLazyQuery<{ user: User }>(
GET_USER,
{
variables: { userId },
fetchPolicy: "network-only",
}
);
React.useEffect(() => {
setUser(latestUserData?.user || propsUser);
}, [latestUserData, propsUser]);
const total = React.useMemo(() => {
return user.purchases
.map((p) => p.price)
.reduce((runningTotal, price) => runningTotal + price, 0);
}, [user]);
useSubscription(SUBSCRIBE_TO_PURCHASE_UPDATES, {
variables: { userId },
shouldResubscribe: false,
onSubscriptionData: async () => {
getUser(); // N.B. if getUser() call is removed, tests run (though failing since query isn't fetched)
},
});
console.log("render");
console.log(user);
return <pre>total: ${total}</pre>;
};
// component.test.tsx
import React from "react";
import { act } from "react-dom/test-utils";
import { MockedProvider } from "@apollo/client/testing";
import { mount, ReactWrapper } from "enzyme";
import { TheComponent } from "./component";
import { mockUserId, getUserMock, subscribetoUserMock } from "./mocks";
describe("thing", () => {
const userId = mockUserId;
const initialUser = { id: userId, purchases: [] };
const mocks = [subscribetoUserMock.success.mock, getUserMock.success.mock];
let wrapper: ReactWrapper;
beforeAll(() => {
wrapper = mount(
<MockedProvider mocks={mocks} addTypename={false}>
<TheComponent user={initialUser} />
</MockedProvider>
);
});
test("should not loop", () => {
expect(wrapper.text()).toBe("total: $0");
});
describe("after a tick", () => {
beforeAll(async () => {
await act(async () => {
await new Promise((res) => setTimeout(res, 0));
wrapper.update();
});
});
test("should show the updated purchase total", () => {
expect(wrapper.text()).toBe("total: $1"); // <---------- never executes
});
});
});
Versions System: OS: macOS 10.15.7 Binaries: Node: 12.16.2 - ~/.nvm/versions/node/v12.16.2/bin/node Yarn: 1.22.4 - ~/.nvm/versions/node/v12.16.2/bin/yarn npm: 6.14.4 - ~/.nvm/versions/node/v12.16.2/bin/npm Browsers: Chrome: 86.0.4240.193 Firefox: 79.0 Safari: 14.0 npmPackages: @apollo/client: ^3.2.5 => 3.2.5 npmGlobalPackages: apollo: 2.30.3
Let me know what other information I can provide to help. I really appreciate all of the power of this library.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:4
- Comments:8 (3 by maintainers)
Top GitHub Comments
@Mathdrquinn Just tried out your reproduction and it does appear to show the problem, so I think we have enough info to take it from here.
On a personal note, I recently declared bankruptcy on my GitHub notifications in order to get them back under control, so thanks for the ping.
I have a similar issue, the onSubscriptionData it’s also triggering infinite renders on my tests.