How does Apollo-Client GraphQL refetchQueries works? - reactjs

Any idea how do we get the response data from refetchQueries? I got the query response data from mutation.
Mutation
import { gql } from 'apollo-boost';
export const DONE_TASK = gql`
mutation DoneTask($taskId: ID!) {
doneTask(input: {
taskId: $taskId
}) {
task {
id
status
}
}
}
`;
Query
import { gql } from 'apollo-boost';
export const GET_TASKS_BY_STATUS = gql`
query GetTasksByStatus($status: String!) {
getTasksByStatus(status: $status) {
edges {
node {
id
status
description
}
}
}
}
`;
Usage
const response = await client.mutate({
mutation: DONE_TASK,
variables: {
taskId: 1
},
refetchQueries: () => [{
query: GET_TASKS_BY_STATUS,
variables: {
status: "OPEN"
},
}]
});
console.log(response);
Output
data: {
doneTask: {
task: { id: 1, status: 'DONE'}
}
}
But I expect a response data from GET_TASKS_BY_STATUS.
🤔 😕

Any queries you refetch through refetchQueries should already be used by some useQuery hook, Query component or graphql HOC. In order to access the data inside the same component as your mutation, you need to utilize the query being refetched inside that same component:
const { data } = useQuery(GET_TASKS_BY_STATUS, { variables: { status: 'OPEN' } })
const [mutate] = useMutation(DONE_TASK,{
variables: {
taskId: 1,
},
refetchQueries: () => [{
query: GET_TASKS_BY_STATUS,
variables: {
status: 'OPEN',
},
}],
})

Related

Run sequental graphql queries using Apollo Client

I am trying to load data using below queries in React.
export const GET_POSTS = gql`
query GetLaunches($limit: Int) {
launches(limit: $limit) {
name
id
date_utc
}
}
`;
I am using above query like below inside React component.
const {loading, error, data} = useQuery(GET_POSTS, {
variables: { limit },
skip: !limit
});
Above return data like below.
data = {
items: [
{
date_utc: "2014-01-06T18:06:00.000Z"
id: ['9D1B7E0']
name: "Thaicom 6"
},
{
date_utc: "2014-01-06T18:06:00.000Z"
id: ['1S1B5D0']
name: "Thai 0"
},
{
date_utc: "2014-01-06T18:06:00.000Z"
id: ['7F1B7E3']
name: "Sun 6"
},
{
date_utc: "2014-01-06T18:06:00.000Z"
id: ['4A1B5K7']
name: "Moon 0"
}
]
}
Now what I want to make another query where I will pass above id as a variable to another query and load description of each individual above list of items.
I tried below. Below is queries.ts file.
import { gql } from 'apollo-boost';
export const GET_POSTS = gql`
query GetLaunches($limit: Int) {
launches(limit: $limit) {
name
id
date_utc
}
}
`;
export const GET_DESCRIPTION = gql`
query GetDescription($id: ID!) {
mission(id: $id) {
description
}
}
`;
Here is Home.tsx file.
import React, { useEffect, useState } from 'react';
import { useQuery } from '#apollo/react-hooks';
import { HomeContainer } from '../Container/';
import { GET_POSTS, GET_DESCRIPTION } from '../Queries/';
export const Home: React.FC = () => {
const limit = 10;
const launches = useQuery(GET_LAUNCHES, {
variables: { limit },
skip: !limit
});
const launchDescription = useQuery(GET_DESCRIPTION, {
variables: { id: launches?.data?.launches?.mission_id?.[0] },
skip: launches.loading || !launches?.data?.launches?.mission_id?.[0],
});
useEffect(() => {
console.log(launchDescription.data); // returns undefined
console.log(launches.data); // this returns correct data
}, [launches]);
return (
<HomeContainer
loading={launches.loading}
error={launches.error}
data={launches.data}
/>
);
}
Inside useEffect hook, I want to add description to each individual launch in launches.data array. How can I make this work?
You need to use optional chaining for launches?.loading or simply just omit this to !launches?.data?.launches?.mission_id?.[0]
Both of your queries right now are fired at the same time. What you need to do is make the second query wait for the first one to be completed (using useLazyQuery), and only fire when the ID is available.
Here is how that should look:
import React, { useEffect, useState } from 'react';
import { useQuery, useLazyQuery } from '#apollo/react-hooks';
import { HomeContainer } from '../Container/';
import { GET_POSTS, GET_DESCRIPTION } from '../Queries/';
export const Home: React.FC = () => {
const limit = 10;
const launches = useQuery(GET_POSTS, {
variables: { limit },
skip: !limit
});
// useLazyQuery provides a function you can use to fire the query
// only when you have what you need for it
const [getLaunchDescription, launchDescription] = useLazyQuery(GET_DESCRIPTION)
useEffect(() => {
// check that you have the id before you ask for the description
if (launches?.data?.launches?.mission_id?.length) {
getLaunchDescription({
variables: { id: launches.data.launches.mission_id[0] }
});
}
}, [launches])
return (
<HomeContainer
loading={launches.loading}
error={launches.error}
data={launches.data}
/>
);
}

how can I pass arguments to the query method in apollo?

I am using apollo client to fetch the data.
And I want it to only get those todos that are made by the logged in user only.
But this piece doesn't work
Source code:
import { gql } from '#apollo/client';
export const todoService = {
getTodoItems: () => gql`
query todoQuery($loggedUserId: String!) {
todo(where: { userId: { _eq: $loggedUserId } }, order_by: { createdAt: desc }) {
id
todo {
title,
body,
status
}
userId
}
}
}
`
Redux thunk file
import { createAsyncThunk } from '#reduxjs/toolkit';
import { apolloClient } from '#/Apollo';
import { todoService } from './_graphql';
export const todoThunk = {
getTodoItems: createAsyncThunk(`db/getTodoItems`, async (loggedUserId: string) => {
const response = await apolloClient.query({
query: todoService.getTodoItems(),
variables: { loggedUserId },
fetchPolicy: `network-only`,
});
return response;
}),
React Component
useEffect(
dispatch(todoThunk.getTodoItems(loggedUserId));
,[dispatch])
However it works when I hard code the userId in place of variable loggedUserId like this:
export const todoService = {
getTodoItems: () => gql`
query todoQuery {
todo(where: { userId: { _eq: "some_hard_coded_id" } }, order_by: { createdAt: desc }) {
id
todo {
title,
body,
status
}
userId
}
}
}
`
It seems you missed a $ sign
try:
import { gql } from '#apollo/client';
export const todoService = {
getTodoItems: () => gql`
query todoQuery($loggedUserId: String!) {
todo(where: { userId: { _eq: $loggedUserId } }, order_by: { createdAt: desc }) {
id
todo {
title,
body,
status
}
userId
}
}
}
`
this worked for me.
import { gql } from '#apollo/client';
export const todoService = {
getTodoItems: () => gql`
query todoQuery($loggedUserId: uuid! = loggedUserId) {
todo(where: { userId: { _eq: $loggedUserId } }, order_by: { createdAt: desc }) {
id
todo {
title,
body,
status
}
userId
}
}
}
`

Get data from a query in ReactJs using GraphQl

I use Apollo Client and have the next code:
import {InMemoryCache, makeVar} from "#apollo/client";
export const cartItemsVar = makeVar(['test']);
export const demo3 = makeVar({name: 123});
export const cache = new InMemoryCache({
addTypename: true,
typePolicies: {
Person: {
fields: {
nameCar: {
read(name) {
return demo3();
}
}
}},
Query: {
fields: {
cartItems: {
read() {
return cartItemsVar();
}
},
demo: {
merge(name) {
return demo3()
}
}
}
}
}
});
How you can notice, this is my store in my react application. I want to get data from
Person: {
fields: {
nameCar: {
read(name) {
return demo3();
}
}
}
},
But doing something like this
const GET_CART_ITEMS = gql `
query GetCartItems {
nameCar #client
}
`;
//
const {
data,
loading,
error
} = useQuery(GET_CART_ITEMS);
... i get undefined in data., but if i do
export const GET_CART_ITEMS = gql `
query GetCartItems {
cartItems #client
}
`;
...i get the wanted result cartItems: ["test"].
Question: How to get data {name: 123} in the first situation and why the code does not work? What is the difference inside the typePolicies between Person and Query?

What is the right way to read cache after writing to cache from react apollo-client?

I have created a basic example that reads data from a graphql server.
I have noticed my network calls make a request after I have run my mutation.
I am trying to update the cache to prevent that network call but when I then try to read the cache.
This method works to retrieve data, but it does not automatically read from the cache:
useQuery
const { data } = useQuery(GET_PLAYERS);
useQuery with #client directive to indicate get cache - fails
const GET_PLAYERS = gql`
query getPlayers {
players #client {
__typename
_id
name
score
}
}
`;
const { data } = useQuery(GET_PLAYERS);
err =
MissingFieldError {message: "Can't find field 'players' on ROOT_QUERY object", path: Array(1), query: {…}, variables: {…}}
message: "Can't find field 'players' on ROOT_QUERY object"
using client.readQuery() returns nothing on both load and after mutation is still no results:
const obj = client.readQuery({ query: GET_PLAYERS });
Here is my full code.
import React, { FC } from 'react';
import 'cross-fetch/polyfill'; // patch for tests: Error: fetch is not found globally and no fetcher passed, to fix pass a fetch for your environment
import {
gql,
useQuery,
useMutation,
ApolloClient,
InMemoryCache,
ApolloProvider
} from '#apollo/client';
const uri = 'http://localhost:4000/graphql';
const client = new ApolloClient({
uri,
cache: new InMemoryCache()
});
const ADD_PLAYER = gql`
mutation AddPlayer($name: String!) {
addPlayer(player: { name: $name }) {
_id
name
score
}
}
`;
// example with directive = #client to try to get cached version
// const GET_PLAYERS = gql`
// query getPlayers {
// players #client {
// __typename
// _id
// name
// score
// }
// }
// `;
const GET_PLAYERS = gql`
query getPlayers {
players {
_id
name
score
}
}
`;
interface IData {
data: {
players: Array<{ name: string }>;
};
loading: boolean;
}
interface IPlayer {
name: string | null;
}
const App: FC = () => {
const { data } = useQuery(GET_PLAYERS);
// const { data } = client.readQuery({ query: GET_PLAYERS });
// console.log('obj = ', obj);
// const data = obj && obj.data;
const [addPlayer] = useMutation(ADD_PLAYER, {
update: (cache, { data: mutationData }) => {
try {
const cacheGetPlayers: { players: Array<IPlayer> } | null = cache.readQuery({
query: GET_PLAYERS
});
const cachePlayers: Array<IPlayer> = (cacheGetPlayers && cacheGetPlayers.players) || [
{ name: null }
];
let playersUpdated: Array<IPlayer> = { ...cachePlayers };
const player = mutationData.addPlayer;
if (player) {
// ie not already existing !!!
if (playersUpdated) {
if (!cachePlayers.find((item: IPlayer) => player.name !== item.name)) {
playersUpdated.push(player);
}
} else {
playersUpdated = [player];
}
cache.writeQuery({
query: GET_PLAYERS,
data: {
getPlayers: {
players: playersUpdated
}
}
});
}
} catch (err) {
console.log('err = ', err);
}
}
});
const handleClick = () => {
const name = 'mary';
addPlayer({
variables: { name },
// example data:
// optimisticResponse: {
// __typename: 'Mutation',
// addPlayer: {
// _id: ObjectId('4edd40c86762e0fb12000003'), // 4edd40c86762e0fb12000003
// score: 0,
// name
// }
// },
refetchQueries: [{ query: GET_PLAYERS }]
});
};
return (
<div>
list of existing names = {data &&
data.players instanceof Array &&
data.players.map((item: { name: string }) => `,${item.name}`)}
<button type="button" onClick={handleClick}>
Click to add player
</button>
</div>
);
};
const Prov = () => (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
export default Prov;
Any advice, thanks
Firstly, #client directive just uses for querying data that you should initiate a players field in the cache with the __typename. Like this:
const cache = new InMemoryCache()
const client = new ApolloClient({
cache,
link,
})
cache.writeData({
data: {
players: {
items: [],
__typename: Player
}
},
})
...
const GET_PLAYERS = gql`
query {
players #client {
__typename
items {
_id
name
score
}
}
}
`;
const { data } = useQuery(GET_PLAYERS);
Secondly, if you want to update the cache after a mutation you should use update option. Like this:
useMutation(UPDATE_PLAYER, {
update(cache, { data: { player } }) {
const { players } = cache.readQuery(options)
cache.writeQuery({
...options,
data: {
players: { ...players, items: [player, ...players.items] },
},
})
},
}
}
)

How return data from a GraphQL mutation?

I have the following redux-form below... How can I get the console.log to obtain the user.id from the graphQL mutation result data?
const withForm = compose(
withMySignupMutation,
reduxForm({
...
onSubmit: async (values, dispatch, {
mySignup,
}) => {
try {
const result = await mySignup(values);
console.log('I Need user.id here');
console.log(result.user.id);
...
withMySignupMutation
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
export const SIGNUP_MUTATION = gql`
mutation (
$email: String!
$name: String!
) {
signup(authProvider: {
mySignup: {
email: $email
name: $name
}
}) {
token
}
}
`;
export default graphql(
SIGNUP_MUTATION,
{
props: ({ mutate }) => ({
mySignup: async (variables) => {
const { data } = await mutate({ variables });
},
}),
}
);
Thank you! happy holidays
In my opinion, it should be result.data.signup.id but we need to see the resolvers and your schema definition. It depends on what the resolvers are sends back. It should be a User type.
Anyway, you must ask for the id in your mutation otherwise it won't get resolved:
export const SIGNUP_MUTATION = gql`
mutation (
$email: String!
$name: String!
) {
signup(authProvider: {
mySignup: {
email: $email
name: $name
}
}) {
token
id ### <===HERE===
}
}
`
And if you have a graphiql access to the server:
click on the top-right corner the DOCS button, then
click mutation: Mutation
then you should see something like:
signup(authProvider: AuthProviderNewcomerData!): User
Then you know you will get a User back.

Resources