Skip to content

Use useSyncExternalStore() #8785

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 16, 2021
3,821 changes: 845 additions & 2,976 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,19 @@
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0",
"react": "^16.8.0 || ^17.0.0",
"subscriptions-transport-ws": "^0.9.0 || ^0.11.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0-beta",
"subscriptions-transport-ws": "^0.9.0 || ^0.11.0",
"use-sync-external-store": "^1.0.0-beta-4ff5f5719-20211115"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"subscriptions-transport-ws": {
"optional": true
},
"use-sync-external-store": {
"optional": true
}
},
"dependencies": {
Expand Down Expand Up @@ -104,6 +108,7 @@
"@types/node": "16.11.7",
"@types/react": "17.0.34",
"@types/react-dom": "17.0.2",
"@types/use-sync-external-store": "^0.0.3",
"bundlesize": "0.18.1",
"cross-fetch": "3.1.4",
"crypto-hash": "1.3.0",
Expand Down
1 change: 1 addition & 0 deletions src/link/persisted-queries/__tests__/react.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** @jest-environment node */
import * as React from 'react';
import * as ReactDOM from 'react-dom/server';
import gql from 'graphql-tag';
Expand Down
17 changes: 12 additions & 5 deletions src/react/components/__tests__/client/Mutation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,11 @@ describe('General Mutation testing', () => {
return (
<Mutation mutation={mutation} refetchQueries={refetchQueries}>
{(createTodo: any, resultMutation: any) => (
<Query query={query} variables={variables}>
<Query
query={query}
variables={variables}
notifyOnNetworkStatusChange={true}
>
{(resultQuery: any) => {
try {
if (count === 0) {
Expand All @@ -1047,13 +1051,16 @@ describe('General Mutation testing', () => {
// mutation loading
expect(resultMutation.loading).toBe(true);
} else if (count === 6) {
// mutation loaded
expect(resultMutation.loading).toBe(false);
// mutation still loading???
expect(resultMutation.loading).toBe(true);
} else if (count === 7) {
expect(resultQuery.loading).toBe(true);
expect(resultMutation.loading).toBe(false);
} else if (count === 8) {
// query refetched
expect(resultQuery.data).toEqual(peopleData3);
expect(resultQuery.loading).toBe(false);
expect(resultMutation.loading).toBe(false);
expect(resultQuery.data).toEqual(peopleData3);
}
count++;
} catch (err) {
Expand All @@ -1074,7 +1081,7 @@ describe('General Mutation testing', () => {
);

waitFor(() => {
expect(count).toEqual(8);
expect(count).toBe(9);
}).then(resolve, reject);
}));

Expand Down
64 changes: 37 additions & 27 deletions src/react/components/__tests__/client/Query.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,11 @@ describe('Query component', () => {
const { variables } = this.state;

return (
<AllPeopleQuery query={query} variables={variables}>
<AllPeopleQuery
query={query}
variables={variables}
notifyOnNetworkStatusChange={true}
>
{(result: any) => {
try {
switch (count) {
Expand Down Expand Up @@ -1719,35 +1723,40 @@ describe('Query component', () => {
<AllPeopleQuery
query={query}
variables={variables}
notifyOnNetworkStatusChange={true}
onCompleted={this.onCompleted}
>
{({ loading, data }: any) => {
switch (renderCount) {
case 0:
expect(loading).toBe(true);
break;
case 1:
case 2:
expect(loading).toBe(false);
expect(data).toEqual(data1);
break;
case 3:
expect(loading).toBe(true);
break;
case 4:
expect(loading).toBe(false);
expect(data).toEqual(data2);
setTimeout(() => {
this.setState({ variables: { first: 1 } });
});
case 5:
expect(loading).toBe(false);
expect(data).toEqual(data2);
break;
case 6:
expect(loading).toBe(false);
expect(data).toEqual(data1);
break;
try {
switch (renderCount) {
case 0:
expect(loading).toBe(true);
break;
case 1:
case 2:
expect(loading).toBe(false);
expect(data).toEqual(data1);
break;
case 3:
expect(loading).toBe(true);
break;
case 4:
expect(loading).toBe(false);
expect(data).toEqual(data2);
setTimeout(() => {
this.setState({ variables: { first: 1 } });
});
case 5:
expect(loading).toBe(false);
expect(data).toEqual(data2);
break;
case 6:
expect(loading).toBe(false);
expect(data).toEqual(data1);
break;
}
} catch (err) {
reject(err);
}
renderCount += 1;
return null;
Expand All @@ -1764,6 +1773,7 @@ describe('Query component', () => {
);

waitFor(() => {
expect(renderCount).toBe(7);
expect(onCompletedCallCount).toBe(3);
}).then(resolve, reject);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** @jest-environment node */
import React from 'react';
import gql from 'graphql-tag';
import { DocumentNode } from 'graphql';
Expand Down
1 change: 1 addition & 0 deletions src/react/components/__tests__/ssr/server.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** @jest-environment node */
import React from 'react';
import {
print,
Expand Down
17 changes: 11 additions & 6 deletions src/react/hoc/__tests__/queries/errors.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,10 @@ describe('[queries] errors', () => {
let iteration = 0;
let done = false;
const ErrorContainer = withState('var', 'setVar', 1)(
graphql<Props, Data, Vars>(query)(
graphql<Props, Data, Vars>(
query,
{ options: { notifyOnNetworkStatusChange: true }},
)(
class extends React.Component<ChildProps<Props, Data, Vars>> {
componentDidUpdate() {
const { props } = this;
Expand All @@ -234,7 +237,7 @@ describe('[queries] errors', () => {
);
} else if (iteration === 3) {
// variables have changed, wee are loading again but also have data
expect(props.data!.loading).toBeTruthy();
expect(props.data!.loading).toBe(true);
} else if (iteration === 4) {
// the second request had an error!
expect(props.data!.error).toBeTruthy();
Expand All @@ -256,8 +259,8 @@ describe('[queries] errors', () => {
render() {
return null;
}
}
)
},
),
);

render(
Expand Down Expand Up @@ -470,9 +473,11 @@ describe('[queries] errors', () => {
});
break;
case 3:
// Second render was added by useSyncExternalStore changes...
case 4:
expect(props.data!.loading).toBeTruthy();
break;
case 4:
case 5:
expect(props.data!.loading).toBeFalsy();
expect(props.data!.error).toBeFalsy();
expect(props.data!.allPeople).toEqual(
Expand All @@ -499,7 +504,7 @@ describe('[queries] errors', () => {
</ApolloProvider>
);

waitFor(() => expect(count).toBe(5)).then(resolve, reject);
waitFor(() => expect(count).toBe(6)).then(resolve, reject);
});

itAsync('does not throw/console.err an error after a component that received a network error is unmounted', (resolve, reject) => {
Expand Down
67 changes: 41 additions & 26 deletions src/react/hoc/__tests__/queries/lifecycle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ describe('[queries] lifecycle', () => {
const Container = graphql<Vars, Data, Vars>(query, {
options: props => ({
variables: props,
fetchPolicy: count === 0 ? 'cache-and-network' : 'cache-first'
fetchPolicy: count === 0 ? 'cache-and-network' : 'cache-first',
notifyOnNetworkStatusChange: true,
})
})(
class extends React.Component<ChildProps<Vars, Data, Vars>> {
Expand Down Expand Up @@ -209,7 +210,10 @@ describe('[queries] lifecycle', () => {
});

const Container = graphql<Vars, Data, Vars>(query, {
options: props => ({ variables: props })
options: props => ({
variables: props,
notifyOnNetworkStatusChange: true,
}),
})(
class extends React.Component<ChildProps<Vars, Data, Vars>> {
componentDidUpdate(prevProps: ChildProps<Vars, Data, Vars>) {
Expand Down Expand Up @@ -307,7 +311,10 @@ describe('[queries] lifecycle', () => {
cache: new Cache({ addTypename: false })
});

const Container = graphql<Vars, Data, Vars>(query)(
const Container = graphql<Vars, Data, Vars>(
query,
{ options: { notifyOnNetworkStatusChange: true } },
)(
class extends React.Component<ChildProps<Vars, Data, Vars>> {
componentDidUpdate(prevProps: ChildProps<Vars, Data, Vars>) {
try {
Expand Down Expand Up @@ -794,11 +801,15 @@ describe('[queries] lifecycle', () => {
}

render() {
count++;
const user = this.props.data!.user;
const name = user ? user.name : '';
if (count === 2) {
expect(name).toBe('Luke Skywalker');
try {
count++;
const user = this.props.data!.user;
const name = user ? user.name : '';
if (count === 3) {
expect(name).toBe('Luke Skywalker');
}
} catch (err) {
reject(err);
}
return null;
}
Expand Down Expand Up @@ -873,26 +884,30 @@ describe('[queries] lifecycle', () => {
<ApolloProvider client={client}>
<QueryComponent query={query}>
{({ loading, data, refetch }: any) => {
if (!loading) {
if (!refetched) {
expect(data.books[0].name).toEqual('ssrfirst');
//setTimeout allows component to mount, which often happens
//when waiting ideally we should be able to call refetch
//immediately However the subscription needs to start before
//we update the data To get around this issue, we would need
//to start the subscription before we render to the page. In
//practice, this seems like an uncommon use case, since the
//data you get is fresh, so one would wait for an interaction
setTimeout(() => {
refetch().then((refetchResult: any) => {
expect(refetchResult.data.books[0].name).toEqual('first');
done = true;
try {
if (!loading) {
if (!refetched) {
expect(data.books[0].name).toEqual('ssrfirst');
//setTimeout allows component to mount, which often happens
//when waiting ideally we should be able to call refetch
//immediately However the subscription needs to start before
//we update the data To get around this issue, we would need
//to start the subscription before we render to the page. In
//practice, this seems like an uncommon use case, since the
//data you get is fresh, so one would wait for an interaction
setTimeout(() => {
refetch().then((refetchResult: any) => {
expect(refetchResult.data.books[0].name).toEqual('first');
done = true;
});
});
});
refetched = true;
} else {
expect(data.books[0].name).toEqual('first');
refetched = true;
} else {
expect(data.books[0].name).toEqual('first');
}
}
} catch (err) {
reject(err);
}
return <p> stub </p>;
}}
Expand Down
9 changes: 6 additions & 3 deletions src/react/hoc/__tests__/queries/loading.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,10 @@ describe('[queries] loading', () => {

const Container = connect(
graphql<Props, Data, Vars>(query, {
options: ({ first }) => ({ variables: { first } })
options: ({ first }) => ({
variables: { first },
notifyOnNetworkStatusChange: true,
})
})(
class extends React.Component<ChildProps<Props, Data, Vars>> {
render() {
Expand Down Expand Up @@ -754,7 +757,8 @@ describe('[queries] loading', () => {
graphql<Props, Data, Vars>(query, {
options: ({ first }) => ({
variables: { first },
fetchPolicy: 'network-only'
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
})
})(
class extends React.Component<ChildProps<Props, Data, Vars>> {
Expand All @@ -774,7 +778,6 @@ describe('[queries] loading', () => {
expect(props.data!.loading).toBeFalsy(); // has initial data
expect(props.data!.allPeople).toEqual(data.allPeople);
break;

case 3:
expect(props.data!.loading).toBeTruthy(); // on variables change
break;
Expand Down
4 changes: 3 additions & 1 deletion src/react/hoc/__tests__/queries/observableQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,9 @@ describe('[queries] observableQuery', () => {
if (count === 2) {
expect(loading).toBe(false);
expect(allPeople).toEqual(dataOne.allPeople);
refetch();
setTimeout(() => {
refetch();
});
}
if (count === 3) {
expect(loading).toBe(false);
Expand Down
Loading