react apollo - clear data object before refetch - reactjs

When using refetch of useQuery hook the data object is still defined. And when using infinite scrolling, only the first page will be refetched.
Is it possible to clear the data object before calling the refech so that we can start fresh?
const { data, loading, error, fetchMore, refetch } = useQuery(GET_ALL_ITEMS, {variables});
getNextPage = async () => { // merges results for infinite scrolling
await fetchMore({ variables,
updateQuery: (previousResult, { fetchMoreResult }) => {
const oldEntries = previousResult.items;
const newEntries = fetchMoreResult.items;
fetchMoreResult.items = [...oldEntries, ...newEntries];
return fetchMoreResult;
},
)
}
Can I do something like refresh = () => { data = null; refetch(); } but without directly mutating state?

I couldn't find a way to clear data but found a different approach in Apollo Client v3 docs.
Basically it's to ignore loaded data when loading or in case of error:
const { data: loadedData, loading, error, fetchMore, refetch } = useQuery(
GET_ALL_ITEMS,
{
variables,
notifyOnNetworkStatusChange: true, // important for adequate loading state
}
);
const data = (loading || error) ? undefined : loadedData;
Note, that in order to make loading work for refetch, you need to pass notifyOnNetworkStatusChange: true option.
It's also possible to get networkStatus from useQuery result and check it like this:
networkStatus === NetworkStatus.refetch
but it seems to work for me with loading and error only.

Related

How to make manual data validation/mutation work with useSWR and Axios?

I have tried everything I could so far, but can't get to make this manual data revalidation/mutation to work using const { mutate } = useSWRConfig().
I have tried setting GlobalConfig on my app, and not, but to no changes.
For example: In my "AddressesList" component, I fetch addresses list using the hook:
const { data, isValidating, error} = useSWR('v1/addresses', fetchMyAddresses, {
revalidateOnFocus: false,
revalidateIfStale: false,
});
where the fetcher is a simple is a simple fetchMyAddresses = () => axiosApiInstance.get('v1/addresses').then(res => res.data);
Then in another component, after creating a new address, I use:
const { mutate } = useSWRConfig();
...
await createNewAddress();
if (success) {
mutate('v1/addresses');
redirect('AddressesList');
}
and on success, I am redirected to the AddressesList. But my data list is not updated with the new address... how is that supposed to work?

React-useForm : No formData are sent on the first request with react-query

As the title mention, I tried to combine react-query and react-useform.
but somehow, form data that are handled by use-form is empty when i tried to send them via api reques. I know there should be something wrong with my code since the data are perfecty sent on the next form submit.
here is my code :
const [formData, setFormData] = useState();
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm({
criteriaMode: 'all',
});
...
const { isFetching, isSuccess, isError, refetch } = useQuery(
'login',
() => userApi.login(formData),
{
onError: (res) => {
if (res.response.data) {
setErrorData(res.response.data.errors);
}
},
onSuccess: (res) => {
const userReqData = res.data.payload.user;
setUser(userReqData);
setErrorData({ errors: {} });
localStorage.setItem(
'access_token',
`Bearer ${res.data.payload.access_token}`
);
setTimeout(() => {
if (userReqData.level === 'admin' || userReqData === 'head_admin') {
navigate('/admin');
} else {
navigate('/me');
}
}, 2000);
},
enabled: false,
retry: false,
}
);
...
function handleLogin(data, e) {
// MAYBE THIS IS THE PROBLEM, formData sould be properly set first , but somehow it doesn't. The 'setFormData' works properly, but the 'formData' state is not updated on the first request(still empty, but not empty on console.log) . Instead, 'formData' is sent on the second request, which is strange.
setFormData(data);
refetch();
// or is there any other way to make refetch send the actual data from the handleLogin parameter right to the useQuery hook?
}
...
return (
<form
onSubmit={handleSubmit(handleLogin)}
>
...
</form>
)
'userApi' is an axios request that have been modifided with custom baseurl and headers, so, basicaly it's just a normal axios request.
library that i used :
react-query : https://react-query.tanstack.com/
https://react-hook-form.com/api/useform
You should use useQuery to fetch data, not to perform actions.
From the docs:
A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server. If your method modifies data on the server, we recommend using Mutations instead.
Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects
Here is a great resource that might help you refactor the code to a mutation.

Unexpected result from apollo react's useQuery method

For a project, where i've implemented authentication by running a GraphQL query inside a AuthenticationProvider from a context, I noticed the query is fetching data twice.
const AuthenticationProvider: FC = props => {
const {
loading,
data
} = useQuery(MeQuery)
if (loading) return null
return <AuthenticationContext.Provider value={{user: data?.me || null}} {...props} />
}
However the query runs perfect, I still wanted to know why it fetches the data twice. I did some googling, and came across this issue, where this answer was provided. I tried the same thing, with the skip option, based if the data is loaded.
const [skip, setSkip] = useState(false)
const {
loading,
data
} = useQuery(MeQuery, { skip })
useEffect(() => {
if (!loading && data?.me) {
setSkip(true)
}
}, [loading, data])
// ...
But when logging in, it stopped working.
const useLoginMutation = () => useMutation(LOGIN_QUERY, { update: (cache, { data }) => {
if (!data) {
return null
}
cache.writeQuery({ query: MeQuery, data: { me: data.login } })
}
})
The cache still get's updated with the right values, but doesn't retrieve the user anymore (null).
const { user } = useContext(AuthenticationContext)
What am I missing here? It seems the query did run and fetched the correct data.
You don't need to use context when you are using apollo useQuery. If you make a query first, then the data fetched will be stored in the cache. You can directly access the data from the cache for the second you run the query. Since useQuery has default fetchPolicy cache-first. Mean its check in the cache first, if no query made before it makes a network request.
If you want to avoid network loading. You can make a top-level component AuthWrapper.
const useUserQuery = () => useQuery(ME_QUERY);
const AuthWrapper = ({children}) => {
const {loading, data} = useUserQuery();
if(loading) return null
return children;
}
export GetUsetInThisComponent = ({}) => {
// Since we have fetched the user in AuthWrapper, the user will be fetched from the cache.
const {data} = useUserQuery();
// No you can access user from data?.user
// Rest of the application logic
}
// Wrap the component like this to avoid loading in the children components
<AuthWrapper>
<GetUserInThisComponent />
</AuthWrapper>

next.js swr I do not want to execute it only at the time of initial drawing

Development Enviroment
・ next.js
・ typescript
・ swr
it uses swr to communicate with swr.
I want to run it only when the value of the query is changed.
but is also executed during the initial drawing.
what can I do to prevent it from running during the initial drawing?
export const useUserQuery = (query: string) => {
const fetcher = (url) => {
Axios.get(url).then((res) => {
return res;
});
const path = `users/?query=${query}`;
const { data, error } = useSWR<IUser>(path);
return {
data: data,
loading: !error && !data,
Error: error,
};
};
};
UPDATED:
use initalData and disable revalidateOnMount then it should work as you expect.
see more in the docs: https://github.com/vercel/swr
It seems that you what to refeatch on variable change.
If you try this, it won't work, because even if token changes, SWR will still use the same key and return the wrong data.
useSWR('/api/user', url => fetchWithToken(url, token))
Instead, you can use an array as the key parameter, which contains multiple arguments of fetcher:
const { data: user } = useSWR(['/api/user', token], fetchWithToken)
See more in docs

Issue with refetching specific GraphQL obvervable query

I'm very new to React and GraphQL(Apollo) and learning the stack.
I have a React Component that loads some page data and there is a "Reload" button on the page.
Problem:
I want to refetch the data when I click on "Reload" button.
However, I could not find a way to refetch the data from queryObservable.
How do I refetch the data from QueryObservable? Is there anyway to call refetch?
I tried using reFetchObservableQueries() however, this reloads the page and refetches everything. Can I specifically refetch only a single observable?
This is my code:
getPageQueryObservable = (contentId, key, paginated = false) => {
const query = contentId ? TreeQuery : RootLevelQuery;
const defaultVariables = 'Test';
const variablesForPagination = {
paginationLimit: INITIAL_PAGE_SIZE,
...defaultVariables
};
return this.props.apolloClient.watchQuery({
query,
variables: paginated === true ? variablesForPagination : defaultVariables
});
};
loadPages = async (paginated = false) => {
const { pages: localPages } = this.state;
const { id, key } = this.props;
const queryObservable = this.getPageQueryObservable(
id,
key,
paginated
);
// I want to ideally call this stuff again..When "Reload" button is clicked.
this.querySubscription = queryObservable.subscribe({
next: ({ data, loading }) => {
....
});
You can call query with the same query and variables but a fetchPolicy of network-only in order to refetch the query. This should update the cache and therefore any relevant Observables.
await client.query({ query, variables, fetchPolicy: 'network-only' })
However, there's really no reason to use watchQuery directly. Instead, you should use the hook API to fetch and refetch your data.
const { data, loading, refetch } = useQuery(query, { variables })
Not only does this reduce boilerplate, but now refetching is as simple as
await refetch()

Resources