Apollo: props/cache doesn't update in refetchQueries - javascript

I'm using apollo's HoCs to achieve my current mutation, but I'm a bit confused on a few errors I'm getting:
this is my query:
import gql from 'graphql-tag';
import { careTeamMember } from '../../fragments/careProvider';
const getFullMemberDetails = gql`
query getFullMemberDetails($id: ID!) {
member(id: $id) {
id
created_at
date_of_birth
first_name
last_name
name
email
phone
verified
informed_consent
// ....
}
}
${careTeamMember}`;
export default getFullMemberDetails;
these are my mutations:
import gql from 'graphql-tag';
const updateMember = gql`
mutation UpdateMember($input: UpdateMemberInput!) {
updateMember(input: $input) {
success
member {
id
name
first_name
last_name
email
phone
date_of_birth
informed_consent
hipaa_privacy_policy
check_in_frequency_days
}
}
}
`;
export default updateMember;
import gql from 'graphql-tag';
const updateMemberInsurancePolicy = gql`
mutation UpdateMemberInsurancePolicy(
$member_id: ID!,
$carrier_name: String,
$insurance_member_id: String,
$insurance_group_id: String,
$plan_name: String,
) {
updateMemberInsurancePolicy(
member_id: $member_id,
carrier_name: $carrier_name,
insurance_member_id: $insurance_member_id,
insurance_group_id: $insurance_group_id,
plan_name: $plan_name,
) {
member_insurance_policy {
id
carrier_name
insurance_group_id
insurance_member_id
plan_name
member {
id
previsit {
id
can_schedule
}
}
}
success
}
}
`;
export default updateMemberInsurancePolicy;
and being used like this
handleMemberSubmit = async (formData): Promise<any> => {
try {
const payload = {
id : this.props.data.member.id,
patch : {
first_name : formData.first_name,
last_name : formData.last_name,
date_of_birth : formData.date_of_birth,
phone : formData.phone,
// ....
payment_preference : formData.payment_preference ? 'OUT_OF_POCKET' : 'INSURANCE',
},
};
const insurancePayload = {
member_id : this.props.data.member.id,
carrier_name : formData.insurance_policy.carrier_name,
plan_name : formData.insurance_policy.plan_name,
insurance_group_id : formData.insurance_policy.insurance_group_id,
insurance_member_id : formData.insurance_policy.insurance_member_id,
};
const [
updateMemberResponse,
submitInsuranceResponse,
] = await Promise.all([
this.props.updateMember(payload),
this.props.submitInsurance(insurancePayload),
]);
const { data: { updateMember } } = updateMemberResponse;
const { data: { updateMemberInsurancePolicy } } = submitInsuranceResponse;
return this.props.addNotification('Member updated!', 'success');
} catch (err) {
return this.props.addNotification(getFirstError(err), 'error');
}
}
and the HoC
graphql(getFullMemberDetails, {
options: (ownProps): Object => ({
id: ownProps.id,
}),
}),
graphql(updateMember, {
props: ({ mutate }): {updateMember: (input: Object) => Promise<any> } => ({
updateMember: (input): Promise<any> => mutate({
variables : { input },
options : (props): Object => ({
refetchQueries: [{
query : getFullMemberDetails,
variables : { id: props.id },
}],
}),
}),
}),
}),
graphql(updateMemberInsurance, {
props: ({ mutate }): {submitInsurance: () => {}} => ({
submitInsurance: (input): Promise<any> => mutate({
variables: { ...input },
}),
}),
}),
This async method works fine, but the getFullMemberDetails query doesn't run after those two queries finish.
However, when I move refetchQueries outside of options in the HoC, like this:
graphql(updateMember, {
props: ({ mutate, ownProps }): {updateMember: (input: Object) => Promise<any> } => ({
updateMember: (input): Promise<any> => mutate({
variables : { input },
refetchQueries: [{
query : getFullMemberDetails,
variables : { id: ownProps.id },
}],
}),
}),
}),
my getFullMemberDetails does run, but i get an error like this:
{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getFullMemberDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"member"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"created_at"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"date_of_birth"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"first_name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"last_name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"email"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"phone"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"verified"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"informed_consent"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"postal_address"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"street_address_1"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"street_address_2"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"city"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"state"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"zip_code"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"country"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"time_zone"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"calendar"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cohort"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"customer"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"basic_visits_covered"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"specialist_visits_covered"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"dependents_allowed"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"contract_term"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"start_at"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"end_at"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"in_person_supported"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"care_team"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"care_navigator"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Frabundle.esm.js:63)
at bundle.esm.js:1246
at bundle.esm.js:1558
at Set.forEach (<anonymous>)
at bundle.esm.js:1556
at Map.forEach (<anonymous>)
at QueryManager.push.../../../node_modules/apollo-client/bundle.esm.js.QueryManager.broadcastQueries (bundle.esm.js:1554)
at bundle.esm.js:1160
and my cache/props doesn't get updated. Is there something I'm doing wrong?

Related

react-query 3 setQueryData doesn't update the cache

I have this query:
export function useCardList() {
return useQuery(['cardList'], fetchCardList());
}
I'm using it in my component:
const { data, isLoading } = useCardList();
// delete
const handleOnDelete = (id: string) => {
const newData = {
...data,
data: data?.data.filter((card) => card.id !== id)
};
queryClient.setQueryData(['cardList'], newData);
};
but setQueryData doesn't work. what do you think? (edited)

TypeError: doc is not a function at addSubCollectionDocument

I'm trying to add a new document to a non-existing sub-collection in a document(solutionsCollection/docID/commentsSubCollection).
But I'm getting this error message when I add a document to the sub-collection:
TypeError: doc is not a function at addSubCollectionDocument
Code to add a new document to a comments sub-collection:
const addSubCollectionDocument = async (docID, doc) => {
dispatch({ type: "IS_PENDING" })
try {
const docRef = doc(db, c, docID)
const colRef = collection(docRef, "comments")
const addedDocument = await addDoc(colRef, doc)
dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
return addedDocument
} catch (error) {
console.log(error)
dispatchIfNotCancelled({ type: "ERROR", payload: error })
return null
}
}
handleSubmit function:
const handleSubmit = async (e) => {
e.preventDefault()
try {
const commentToAdd = {
id: Math.floor(Math.random() * 10000),
content: newComment.trim(),
reactions: [],
user: {
userID: user.uid,
avatarURL: user.photoURL,
displayName: user.displayName,
username: user.reloadUserInfo.screenName,
},
replies: [],
createdAt: new Date(),
}
await addSubCollectionDocument(id, commentToAdd)
setNewComment("")
if (response) {
console.log(response.error)
}
} catch (error) {
console.log(error)
}
}
useFirestore hook code:
import { useEffect, useReducer, useState } from "react"
import {
addDoc,
collection,
deleteDoc,
doc,
serverTimestamp,
updateDoc,
} from "firebase/firestore"
import { db } from "../firebase/config"
export const useFirestore = (c) => {
const [response, dispatch] = useReducer(firestoreReducer, initialState)
const [isCancelled, setIsCancelled] = useState(false)
// only dispatch is not cancelled
const dispatchIfNotCancelled = (action) => {
if (!isCancelled) {
dispatch(action)
}
}
// add a document
const addDocument = async (doc) => {
dispatch({ type: "IS_PENDING" })
try {
const createdAt = serverTimestamp()
const addedDocument = await addDoc(collection(db, c), {
...doc,
createdAt,
})
dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
} catch (err) {
dispatchIfNotCancelled({ type: "ERROR", payload: err.message })
}
}
// Add document to sub collection
const addSubCollectionDocument = async (docID, doc) => {
dispatch({ type: "IS_PENDING" })
try {
const docRef = doc(db, c, docID)
const colRef = collection(docRef, "comments")
const addedDocument = await addDoc(colRef, doc)
dispatchIfNotCancelled({ type: "ADDED_DOCUMENT", payload: addedDocument })
return addedDocument
} catch (error) {
console.log(error)
dispatchIfNotCancelled({ type: "ERROR", payload: error })
return null
}
}
useEffect(() => {
return () => setIsCancelled(true)
}, [])
return {
addDocument,
addSubCollectionDocument,
response,
}
}
File where I'm importing addSubCollectionDocument hook:
import { useFirestore } from "../../hooks/useFirestore"
import { useAuthContext } from "../../hooks/useAuthContext"
const SolutionComments = ({ solution }) => {
const [newComment, setNewComment] = useState("")
const { user } = useAuthContext()
const { id } = useParams()
const { addSubCollectionDocument, response } = useFirestore("solutions")
const handleSubmit = async (e) => {
e.preventDefault()
try {
const commentToAdd = {
id: Math.floor(Math.random() * 10000),
content: newComment.trim(),
reactions: [],
user: {
userID: user.uid,
avatarURL: user.photoURL,
displayName: user.displayName,
username: user.reloadUserInfo.screenName,
},
replies: [],
createdAt: new Date(),
}
await addSubCollectionDocument(id, commentToAdd)
setNewComment("")
if (response) {
console.log(response.error)
}
} catch (error) {
console.log(error)
}
}
return (...)}
Package.json gist:
https://gist.github.com/rishipurwar1/57fbf348cd9755a940e7e3f218de570f
Firebase rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /challenges/{challenge}{
allow read: if true
}
match /resources/{resource}{
allow read: if true
}
match /solutions/{solution}{
allow read: if true
allow create: if request.auth.uid != null;
allow update, delete: if request.auth.uid == resource.data.userID;
}
match /users/{userId}{
allow create: if true
allow read: if true
}
}
}
const addSubCollectionDocument = async (docID, doc) => { ... })
// That "doc" parameter is not a function ^^^
That parameter seems to be an object that you are passing to addSubCollectionDocument(). Try renaming that to something else like:
const addSubCollectionDocument = async (docID, docData) => {
const colRef = collection(db, c, docID, "comments")
const addedDocument = await addDoc(colRef, doc)
})
You haven't specified security rules for your comments sub-collection. Try adding the following rules:
match /c/{docId}/comments/{commentId} {
allow read, write: if true;
}
Do update the if true to required rules as per your use case.

React Native - {"name":"Invariant Violation","framesToPop":1}

I'm trying to implement meilisearch api in React native and it is working fine with my simulator and after I publish the app some of the users cannot see the data returning from meilisearch, the error is
{"name":"Invariant Violation","framesToPop":1}
This is my code
Meilisearch.js
import axios from 'axios';
import { meilisearchConfig } from '../Config';
const MeilisearchApi = async (payload, success, failed) => {
try {
const response = await axios({
method: 'post',
url: `${meilisearchConfig?.host}indexes/activities/search`,
data: payload,
headers: {
'X-Meili-API-Key': meilisearchConfig?.apiKey,
},
});
success?.(response?.data);
} catch (err) {
failed?.(err);
}
};
export default MeilisearchApi;
This is the normalizer for returning data
import moment from 'moment';
import { IActivity, IByDateGroupFilter } from 'reducers/types';
export const activityNormalizer = (state, { hits, offset }) => {
const {
melisearchActivityData: { byDate, dates, all },
} = state;
const isRefreshing = offset === 0;
const newAll = isRefreshing ? hits : [...all, ...hits];
const datesNew: string[] = isRefreshing ? [] : dates;
const byDateNew: any = isRefreshing ? {} : byDate;
const byDateGroup: IByDateGroupFilter[] = [];
hits.forEach((activity: IActivity) => {
const date = getFormattedDate(activity.created_at);
if (byDateNew[date]) byDateNew[date].push({ ...activity });
else {
byDateNew[date] = [{ ...activity }];
datesNew.push(date);
}
});
Object.keys(byDateNew).forEach((key) => {
byDateGroup.push({
title: key,
data: byDateNew[key],
});
});
return {
dates: datesNew,
byDate: byDateNew,
byDateGroup,
all: newAll,
};
};
This is how i call my Meilisearch API method
MeilisearchApi(
{
q: search,
filters: filters,
offset: newOffset,
limit: PAGE_SIZE,
},
({ hits }: { hits: any[] }) => {
setDataLoaded(true);
setMelisearchActivitiesToRedux({ hits, offset: newOffset });
if (newOffset === 0) {
sectionList?.current?.scrollToLocation({
itemIndex: 1,
});
}
},
(err: any) => {
setDataLoaded(true);
log(err)
},
);
No Idea how this error happens, when users kill the app and logging again this works fine

passing props as graphql mutation argument in react

My Code looks like this:
interface MutationProps{
username: any,
Mutation: any
}
const UseCustomMutation: React.FC<MutationProps> = (MutationProps: MutationProps) => {
const [myFunc, {data, error}] = useMutation(MutationProps.Mutation);
useEffect(() => {
myFunc({variables:{username: MutationProps.username}})
console.log(JSON.stringify(data))
console.log(JSON.stringify(error, null , 2))
}, [])
return data
}
export const DisplayUser = () => {
const GET_USER = gql`
mutation GetUser($username: String!){
getUser(username: $username) {
pfp
username
password
age
CurrentLive
ismod
description
fullname
}
}
`
const {username} : {username: any} = useParams()
const MyData = UseCustomMutation(username, GET_USER)
console.log(JSON.stringify(MyData))
But I get this error back: ×
Argument of undefined passed to parser was not a valid GraphQL DocumentNode. You may need to use >'graphql-tag' or another method to convert your operation into a document
How about your code looks like this:
interface MutationProps {
username: string;
Mutation: any;
}
const UseCustomMutation: React.FC<MutationProps> = ({ username, Mutation }) => {
const [functionForDoingAction, { data, loading, error }] = useMutation(
Mutation,
{
variables: {
username,
},
}
);
useEffect(() => {
// fn trigger for change data
functionForDoingAction({
variables: {
username: "string_value",
},
});
console.log(JSON.stringify(data));
console.log(JSON.stringify(error, null, 2));
}, []);
if (loading) return "loading...";
if (error) return `Submission error! ${error.message}`;
return data;
};
export const DisplayUser = () => {
const GET_USER = gql`
mutation GetUser($username: String!) {
getUser(username: $username) {
pfp
username
password
age
CurrentLive
ismod
description
fullname
}
}
`;
const { username }: { username: string } = useParams();
const MyData = UseCustomMutation(username, GET_USER);
console.log(JSON.stringify(MyData));
};
you can pass an argument directly to the useMutation hook which they provide as an Options parameter. Or is the direct trigger function from the hook you get.

Using Typescript generic types

I have an interface that describes basic database CRUD operations like so:
export interface IUserDB {
insert: (params: UserAttributes) => Promise<UserResult>
findById: ({ id }: { id: number }) => Promise<UserResult | null>
findByEmail: ({ email }: { email: string }) => Promise<UserResult | null>
remove: ({ id }: { id: number }) => Promise<any>
update: ({
id,
...changes
}: { id: number } & UserAttributes) => Promise<UserResult>
}
I then go ahead to use this interface in this manner:
const makeAddUser = ({ usersDb }: { usersDb: IUserDb }) => {
return async function addUser(userDetails: User) {
const user = makeUser(userDetails)
const exists = await usersDb.findByEmail({ email: user.getEmail() })
if (exists) {
throw new UniqueConstraintError('Email')
}
const newUser = await usersDb.insert({
name: user.getName(),
email: user.getEmail(),
password: user.getPassword(),
username: user.getUsername(),
})
const { id } = newUser.user
await publisher(id.toString(), 'newuser.verify')
await consumer('verify_queue', verifyUser, '*.verify')
return newUser
}
}
I want to be able to reuse this interface for PostDb and CommentDb without me having to write another interface that describes a Post database operation, how can I achieve this? I read about Typescript Generics, but I am not really sure how to apply it to this case, any help will be appreciated. I am still trying to get a hang of Typescript. Thank you very much!
Not really sure how to explain it other than to just show you, but here is what I would do (with some filler code to make things work, but just focus on the interfaces). But basically you define an interface, and pass in the types you want it to support inside <>. So below I do
export interface IDB<Attributes, Result> {
which means you pass in two additional types to this:
interface IUserDb extends IDB<UserAttributes, UserResult>
and now IUserDb is an IDB where any place IDB has Attributes it instead uses UserAttributes, and same with Result and UserResult:
https://codesandbox.io/s/typescript-playground-export-forked-nej0s?file=/index.ts:0-1569
export interface IDB<Attributes, Result> {
insert: (params: Omit<Attributes, 'id'>) => Promise<Result>
findById: ({ id }: { id: number }) => Promise<Result | null>
remove: ({ id }: { id: number }) => Promise<any>
update: ({
id,
...changes
}: { id: number } & Attributes) => Promise<Result>
}
interface UserAttributes {
id: string;
name: string;
username: string;
email: string;
password: string;
}
interface UserResult extends UserAttributes {
getEmail(): string
getName(): string
getPassword(): string
getUsername(): string
}
export interface IUserDb extends IDB<UserAttributes, UserResult>{
findByEmail: ({ email }: { email: string }) => Promise<UserResult | null>
}
const makeUser = (user: UserAttributes): UserResult => {
const u: any = { ...user };
u.getName = () => u.name;
u.getEmail = () => u.email;
u.getPassword = () => u.password;
u.getUsername = () => u.username;
return u as UserResult;
}
const UniqueConstraintError = (err: string) => new Error(`Unique Constraint Error: ${err}`);
const makeAddUser = ({ usersDb }: { usersDb: IUserDb }) => {
return async function addUser(userDetails: UserAttributes) {
const user = makeUser(userDetails)
const exists = await usersDb.findByEmail({ email: user.getEmail() })
if (exists) {
throw UniqueConstraintError('Email')
}
const newUser = await usersDb.insert({
name: user.getName(),
email: user.getEmail(),
password: user.getPassword(),
username: user.getUsername(),
})
return newUser
}
}

Categories

Resources