Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions src/__tests__/useMutation-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ const TASKS_MOCKS = [
},
},
},

{
request: {
query: gql`
mutation AddTaskMissingFieldMutation($input: AddTaskMutationInput!) {
addTask(input: $input) {
id
text
__typename
}
}
`,
variables: { input: { text: 'Learn Jest' } },
},
result: {
data: {
__typename: 'Mutation',
addTask: {
__typename: 'Task',
id: '4',
text: 'Learn Jest',
},
},
},
},
];

const TASKS_QUERY = gql`
Expand Down Expand Up @@ -115,6 +140,15 @@ const ADD_TASK_MUTATION = gql`
}
`;

const ADD_TASK_MISSING_FIELD_MUTATION = gql`
mutation AddTaskMissingFieldMutation($input: AddTaskMutationInput!) {
addTask(input: $input) {
id
text
}
}
`;

interface TaskFragment {
id: number;
text: string;
Expand Down Expand Up @@ -254,3 +288,194 @@ it('should allow to pass options forwarded to the mutation', async () => {
expect(container.querySelectorAll('li')).toHaveLength(4);
expect(container.querySelectorAll('li')[3].textContent).toBe('Learn Jest');
});

it('should have an error if the mutation has a field missings', async () => {
// Added 1 more time the query mock for TASKS_QUERY, because of this comment
// https://github.com/apollographql/react-apollo/issues/617#issuecomment-29310361
TASKS_MOCKS.push({
request: {
query: gql`
query TasksQuery {
tasks {
id
text
completed
__typename
}
}
`,
variables: {},
},
result: {
data: {
__typename: 'Query',
tasks: [
...SAMPLE_TASKS,
{
__typename: 'Task',
id: 4,
text: 'Learn Jest',
} as any,
],
},
},
});

let tasks;
function TasksWithMutation() {
const { data, error, loading } = useQuery(TASKS_QUERY);
const addTask = useMutation<any, { input: Partial<TaskFragment> }>(
ADD_TASK_MISSING_FIELD_MUTATION,
{
update: (proxy, mutationResult) => {
const previousData = proxy.readQuery<{ tasks: TaskFragment[] }>({
query: TASKS_QUERY,
});
previousData!.tasks.push(mutationResult!.data!.addTask);
proxy.writeQuery({ data: previousData, query: TASKS_QUERY });
},
variables: {
input: {
text: 'Learn Jest',
},
},
}
);

tasks = data.tasks;
if (error) {
throw error;
}

if (loading) {
return <>Loading</>;
}

return (
<>
<ul>
{data &&
data.tasks &&
data.tasks.map((task: TaskFragment) => (
<li key={task.id}>{task.text}</li>
))}
</ul>
<button data-testid="add-task-button" onClick={() => addTask()}>
Add new task
</button>
</>
);
}

const client = createClient({ mocks: TASKS_MOCKS });
const { container, getByTestId } = render(
<ApolloProvider client={client}>
<TasksWithMutation />
</ApolloProvider>
);

await wait();

const addTaskButton = getByTestId('add-task-button');
fireEvent.click(addTaskButton);
await wait();

expect(container.querySelectorAll('li')).toHaveLength(0);
expect(tasks).toBe(undefined);
});

it('should work if the mutation has a field missings and the partialRefetch is true', async () => {
// Added 1 more time the query mock for TASKS_QUERY, because of this comment
// https://github.com/apollographql/react-apollo/issues/617#issuecomment-29310361
TASKS_MOCKS.push({
request: {
query: gql`
query TasksQuery {
tasks {
id
text
completed
__typename
}
}
`,
variables: {},
},
result: {
data: {
__typename: 'Query',
tasks: [
...SAMPLE_TASKS,
{
__typename: 'Task',
id: 4,
text: 'Learn Jest',
} as any,
],
},
},
});

let tasks;
function TasksWithMutation() {
const { data, error, loading } = useQuery(TASKS_QUERY, {
partialRefetch: true,
});
const addTask = useMutation<any, { input: Partial<TaskFragment> }>(
ADD_TASK_MISSING_FIELD_MUTATION,
{
update: (proxy, mutationResult) => {
const previousData = proxy.readQuery<{ tasks: TaskFragment[] }>({
query: TASKS_QUERY,
});
previousData!.tasks.push(mutationResult!.data!.addTask);
proxy.writeQuery({ data: previousData, query: TASKS_QUERY });
},
variables: {
input: {
text: 'Learn Jest',
},
},
}
);

tasks = data.tasks;
if (error) {
throw error;
}

if (loading) {
return <>Loading</>;
}

return (
<>
<ul>
{data &&
data.tasks &&
data.tasks.map((task: TaskFragment) => (
<li key={task.id}>{task.text}</li>
))}
</ul>
<button data-testid="add-task-button" onClick={() => addTask()}>
Add new task
</button>
</>
);
}

const client = createClient({ mocks: TASKS_MOCKS });
const { getByTestId } = render(
<ApolloProvider client={client}>
<TasksWithMutation />
</ApolloProvider>
);

await wait();

const addTaskButton = getByTestId('add-task-button');
fireEvent.click(addTaskButton);
await wait();

expect(tasks).toHaveLength(4);
});
24 changes: 24 additions & 0 deletions src/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface QueryHookOptions<TVariables, TCache = object>
ssr?: boolean;
skip?: boolean;
suspend?: boolean;
partialRefetch?: boolean;
}

export interface QueryHookResult<TData, TVariables>
Expand All @@ -67,6 +68,7 @@ export function useQuery<
ssr = true,
skip = false,
suspend = false,
partialRefetch = false,

// Watch options
pollInterval,
Expand Down Expand Up @@ -163,6 +165,28 @@ export function useQuery<
};
}

// Taken from https://github.com/apollographql/react-apollo/blob/22f8ebf52b26b348d6be905d5b7fbbfea51c1541/src/Query.tsx#L455-L472
if (
partialRefetch &&
Object.keys(result.data).length === 0 &&
result.partial &&
fetchPolicy !== 'cache-only'
) {
data = {
...result.data,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are empty so maybe it would be better, to keep stale data from previous result using useRef.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, sorry to invade this PR without introduction. I didn't catch it in time and coded basically the same thing yesterday evening. So now I'm just sharing my experience. Thanks for the PR!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments! I'll update the PR with this! is much better.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW Also this one is related to the need of showing stale data:
#134

...(observableQuery.getLastResult() || {}).data,
};

const partialRefetchResult = {
...helpers,
data,
loading: true,
networkStatus: NetworkStatus.loading,
};
partialRefetchResult.refetch();
return partialRefetchResult;
}

return {
...helpers,
data,
Expand Down