React Apollo and conditional Boolean variables - reactjs

I have this Apollo Query and the GraphQL query below... And it works, but unfortunately not as I desire ...
Now I get "WHERE": casinos has popular=true/false AND mobile=true/false AND awarded=true/false and so on ... (with emphasisis on "AND")
popular, mobile, awarded and live are checkboxes on a webpage which I use to filter results which updates when the user check/uncheck them.
What I want
When a user checks for example popular and awarded, I want to show all the casinos which have awarded set to true, and all casinos which has popular set to true. NOT casinos which has popular AND rewarded set to true.
How?
React/JS:
<Query query={CASINO_QUERY}
variables={{
popular: this.state.popular,
awarded: this.state.awarded,
mobile: this.state.mobile,
live: this.state.live
}}>
{({ data, loading, error }) => {
if (loading) return <p>Loading…</p>;
if (error) return <p>Something went wrong</p>;
return (
<div>
{data.casinos.map((casino) => (
<Featured key={casino.id} casino={casino} />
))}
</div>
);
}}
</Query>
GraphQL query:
import { gql } from "#apollo/client";
const CASINO_QUERY = gql`
query casinos($popular: Boolean, $awarded: Boolean, $mobile: Boolean, $live: Boolean ) {
casinos(where: {
Popular: $popular
Awarded: $awarded
Mobile: $mobile
Live: $live
})
{
slug
name
logo {
url
}
rating
owner
founded
payout_percentage
expert_consensus
}
}
}
`;
export default CASINO_QUERY;

Related

React Query w/NextJS - I'm noticing that, once my page has loaded, the query does not execute until I have clicked within the browser's viewport

I'm new to React Query and am wondering what that's about--forestalling execution of my GraphQL query until I click anywhere within the browser's viewport. Shouldn't the query just execute straight away?
Here's my code:
import { useUrlHashParameters } from "../src/hooks";
import { useQuery } from "react-query";
import { gql, request } from "graphql-request";
export default function Home() {
const parameters = useUrlHashParameters();
const { data } = useQuery("test", async () => {
if (parameters) {
localStorage.setItem("accessToken", parameters.accessToken);
const url = "http://localhost:3001/graphql";
const document = gql`
query {
products {
id
title
description
totalInventory
__typename
}
}
`;
const requestHeaders = {
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
};
return await request({
url,
document,
requestHeaders,
});
}
});
console.log(data);
return (
<div className={styles.container}>
<main className={styles.main}>
<Image
src="/blah-logo.png"
alt="Blah"
width={300}
height={75}
/>
{data?.products.map(({ title }, index: number) => (
<div key={index}>{title}</div>
))}
</main>
</div>
);
}
Shouldn't the query just execute straight away?
Yes, it should, and it likely does. My best guess is that on the first render, the parameters are undefined, and since you check for if (parameters) in the queryFn without doing anything in an else branch, the queryFn will just return a Promise that resolves to undefined (which will be illegal in v4 btw).
Then, when you focus the window, you get a refetch, in which case parameters likely exist.
You can verify this by putting a log statement into the queryFn, but outside the if statement.
Further, all parameters that your query uses should be part of the query key. That makes sure that you get automatic refetches when the parameters are changing. So changing your query key to:
["test", parameters]
and setting: enabled: !!parameters instead of the if to stop the query from running if there are no parameters is likely the best course of action:
const parameters = useUrlHashParameters();
const { data } = useQuery(
["test", parameters],
() => request({...}),
{
enabled: !!parameters
}
)

React/Apollo fetchMore reloading the whole page

I have a problem trying to do a pagination / fetchMore button for a list of data on my webapp. My problem is that when I click on the fetchMore button the whole page is reloading, old school style, while I would like only the updated list of 'calculs' to be reloaded but not the rest of the query (users...).
Is there a way to limit what is updated after a fetchMore ? Don't want the whole DOM to be rerendered.
I also tried to send a specific query, only refecthing what I need, but I got an error as well.
Here is the needed part of the code:
export const query = gql`
query UserAndData($limit: Int!) {
user {
_id,
nom,
prenom,
email,
isAdmin
}
adminCalculations {
_id,
lastUpdated,
comments
}
adminUsers {
_id,
lastConnect,
nom,
prenom,
email,
societe
}
calculs(limit: $limit) {
_id,
userId,
date,
duration,
user {
email
}
}
count
}
`
class Admin extends Component {
state = {
limit : 100,
}
render() {
return(
<Query query={query} variables={{ limit : this.state.limit}} fetchPolicy="cache-and-network">
{({ loading, error, data, fetchMore }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
console.log(data);
if (!data || (!data.user || !data.user.isAdmin)){
return (<Redirect to="/" />)
}
return(
<div className="admin">
(lot of coding and rendering users list, calculs lists, etc...)
<div className="bouton" onClick={() => {
fetchMore({
variables : { limit : this.state.limit + 100 },
updateQuery : (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev;
return Object.assign({}, prev, { calculs :
fetchMoreResult.calculs });
}
})
this.setState({ limit : this.state.limit + 100 })
}
}>Charger plus de calculs</div>
</div>
</div>
</div>
</div>
)
}}
</Query>
)
}
}
export default Admin;
Actually, I found out the problem was linked to my query. As I was querying the whole needed data for my Component and as I had a
if(loading)
statement, the whole page was loading again each time the refecthing was taking place and therefore the whole page was rendering the
<p>Loading...</p>
even if the loading time was very small -> that caused my full page "refreshed"
In order to solve this issue I have created a child component that include also an Apollo Query component but only the query that I want to be refetched. The parent component with the user, adminCalculations and adminUsers query is not pointlessly refreshed and only the needed child query refetches and rerenders.

refetching a query with react-apollo: why is `loading` not true?

I'm trying Apollo and using the following relevant code:
const withQuery = graphql(gql`
query ApolloQuery {
apolloQuery {
data
}
}
`);
export default withQuery(props => {
const {
data: { refetch, loading, apolloQuery },
} = props;
return (
<p>
<Button
variant="contained"
color="primary"
onClick={async () => { await refetch(); }}
>
Refresh
</Button>
{loading ? 'Loading...' : apolloQuery.data}
</p>
);
});
The server delays for 500ms before sending a response with { data: `Hello ${new Date()}` } as the payload. When I'm clicking the button, I expect to see Loading..., but instead the component still says Hello [date] and rerenders half a second later.
According to this, the networkStatus should be 4 (refetch), and thus loading should be true. Is my expectation wrong? Or is something regarding caching going on that is not mentioned in the React Apollo docs?
The project template I'm using uses SSR, so the initial query happens on the server; only refetching happens in the browser - just if that could make a difference.
I think that you need to specify notifyOnNetworkStatusChange: true in the query options (it's false by default).

Store single property from GraphQL query in local Apollo store

In my React app when a user goes to myapp.com/org/companyname it loads a page with the following logic to get some basic details about the company based on the company name in the URL:
const FIND_COMPANY = gql`
query CompanyQuery($companyName: String!) {
company(name: $companyName) {
... on Company {
name
description
}
... on Error {
errorMessage
}
}
}
`;
<Query
query={FIND_COMPANY}
variables={{
companyName: this.props.location.pathname.split("org/")[1]
}}
>
{({ data }) => {
if (data.company.errorMessage)
return <div>{data.company.errorMessage}</div>;
return (
<div>
<h1>{data.company.name}</h1>
<p>{data.company.description}</p>
</div>
);
}}
</Query>
This works fine, however I want to store just the currently company name in the local Apollo state so that it can be easily accessed by other components without having to do string manipulation on the URL in each component.
To do this I've followed the 'Direct writes' section of the Apollo local state docs and ended up with the following:
<ApolloConsumer>
{client => (
<Query
query={FIND_COMPANY}
variables={{
companyName: this.props.location.pathname.split("org/")[1]
}}
onCompleted={data =>
client.writeData({ data: { companyName: data.company.name } })
}
>
{({ data }) => {
if (data.company.errorMessage)
return <div>{data.company.errorMessage}</div>;
return (
<div>
<h1>{data.company.name}</h1>
<p>{data.company.description}</p>
</div>
);
}}
</Query>
}
</ApolloConsumer>
Where I write just the company name to the Apollo local state onCompleted. However, when I run this it crashes the app.
Is this the correct way to store local state in Apollo or have I misunderstood how local Apollo state should be used?

Apollo React Nested Query & Mutation

I have a component that I'd like to fetch data for a profile and allow posting messages on the same view. I've created a simple profile component here, and have the mutation set within the query, similar to the Apollo React tutorial.
When I run this, I get a properly rendered query with data. When the button is pressed, the mutation occurs but the page re-renders with an empty object in the Query's data parameter and the page errors with Cannot read property 'name' of undefined (which is expected given a blank data object).
Is there a better approach here?
const GET_PROFILE = gql`
query GetProfileQuery($profileId: ID!) {
getProfileInfo(profileId: $profileId) {
name
}
}
`;
const ADD_ENDORSEMENT = gql`
mutation AddEndorsement($profileId: ID!, $body: String!) {
addEndorsement(profileId: $profileId, body: $body) {
endorsementId
}
}
`;
const Profile = props => {
const profileId = props.match.params.profileId;
return (
<Query query={GET_PROFILE} variables={{ profileId: profileId }}>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return (
<Fragment>
<h1>Welcome {data.getProfileInfo.name}</h1>
<Mutation mutation={ADD_ENDORSEMENT}>
{(addEndorsement, { loading, error }) => (
<div>
<button
onClick={e => {
addEndorsement({
variables: {
profileId: profileId,
body: "This is a test."
}
});
}}
>
Save
</button>
{loading && <p>Loading...</p>}
{error && <p>Error :(</p>}
</div>
)}
</Mutation>
</Fragment>
);
}}
</Query>
);
};
I had a similar issue were data turned up undefined. In my case the problem was caused by a Query subscribeToMore option I was using invalidating the cache by silently failing to merge the updated data into the original Query results (it did not contain exactly identical data fields).
The question doesn't mention subscriptions, but it will be a similar cache invalidation problem.
To fix I'd first try making sure the Query and Mutation's return identical data.
If that doesn't resolve, then depending on responsiveness needs either look at the "refetchQueries/awaitRefetchQueries" to refresh the Query, or manually poking the updated data into the local cache with "update" options on the Mutation.
Good luck.
Try adding the following to the onClick mutation, it should help avoid needing to update the cache manually, although that would be the next step. You can pass just the string name of the query to refetchQueries, you don't need to pass the actual query there. Another trick is to ensure you are getting the same fields back so the cache object matches, (not sure what type you are returning on the query/mutation.
onClick={e => {
addEndorsement({
variables: {
profileId: profileId,
body: 'This is a test.',
},
refetchQueries: ['GetProfileQuery']
})
}}

Resources