Apollo graphql subscribeToMore doesn't receive the new subscriptionData - reactjs

Why the subscriptionData inside the updateQuery parameter is receiving the query data and not the data from the subscription ?
The query:
const {
data,
error: homePageError,
loading: homePageLoading,
refetch: refetchRaw,
subscribeToMore,
} = useHomePageQuery({
fetchPolicy: "cache-and-network",
notifyOnNetworkStatusChange: true,
skip: !currentAccountId || screen !== "HOME",
variables: {
accountId: currentAccountId ?? "none",
},
});
the subscription:
useEffect(() => {
subscribeToMore({
document: gql`
subscription HomePageRemoteChargeCreated($userId: ID!) {
remoteChargeCreated(userId: $userId) {
remoteCharge {
id
station {
id
name
}
}
}
}
`,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev;
const createdRemoteCharge = subscriptionData.data.remoteChargeCreated;
return {
me: {
...prev.me,
account: {
...prev.me?.account,
accountUser: {
...prev.me?.account?.accountUser,
currentRemoteCharge: {
id: createdRemoteCharge?.id,
station: { ...createdRemoteCharge.station },
},
},
},
},
};
},
variables: {
userId: currentUserId,
},
});
}, [client, currentAccountId, currentUserId, subscribeToMore]);
What I get in subscriptionData type (I should received the content declared inside of the subscription):
(parameter) subscriptionData: {
data: HomePageQuery;
}
I'm using "#apollo/client": "3.7.1" and "react": "18.2.0",
Is anyone having a clue on this ?

Related

Can't get collection array from mongoDB with mongoose query

I have problem with getting data from DB. I want to get "collections" Array from mongoDB and render it in table component, but query returns null because of problem "user not found". Interesting thing is that if I use {email: req.body.email} in updateOne query to search for user and then create new collection it works and user is found.
getCollections.js
const router = require("express").Router();
const User = require("../models/user");
router.get("/", (req, res) => {
var query = { email: req.body.email };
User.find(query, (err, result) => {
if (err) {
res.json({ status: "error", error: "User not found" }, err);
} else {
res.json(result);
}
});
});
module.exports = router;
frontend getCollections query
useEffect(() => {
const url = "http://localhost:5000/api/getCollections";
// const url = `https://item-collection-app-bz.herokuapp.com/api/getCollections`;
axios
.get(url, { email: localStorage.getItem("email") })
.then((response) => {
setListOfCollections(response.data);
});
});
user.js UserSchema
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
username: { type: String, require: true },
password: { type: String, require: true },
email: { type: String, require: true },
admin: { type: Boolean },
blocked: { type: Boolean },
collections: [
{
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
coll_name: { type: String },
type: { type: String },
coll_desc: { type: String },
coll_image: { type: String },
items: [
{
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
item_name: { type: String },
item_desc: { type: String },
comments: [
{
user: { type: String },
comment: { type: String },
comment_id: { type: String },
},
],
likes: { type: Number },
item_image: { type: String },
upload_date: { type: String },
},
],
},
],
});
userSchema.methods.generateAuthToken = function () {
const appToken = jwt.sign({ _id: this._id }, process.env.JWTPRIVATEKEY, {
expiresIn: "7d",
});
return appToken;
};
const User = mongoose.model("user", userSchema);
module.exports = User;
mongoDB
mongoDB structure
Tried User.findOne(), User.find()
SOLUTION
Thank you #Varun Kaklia. The solution is changing router.get and axios.get to router.post and axios.post.
Hey #1zboro1 first check did you receive any data from frontend inside Routes like-
const router = require("express").Router();
const User = require("../models/user");
router.get("/", (req, res) => {
const email = req.body.email;
console.log("Email from frontend", email)
var query = { email: req.body.email };
if (email != null || undefined) {
try {
const user = await User.find({ email});
console.log("User Details in User Routes of Backend",user)
if (user.length > 0) {
const currentUser = {
name: user[0].name,
email1: user[0].email1,
isAdmin: user[0].isAdmin,
_id: user[0]._id,
};
// console.log("Get User in User Routes of Backend", currentUser)
res.status(200).send(currentUser);
}
} catch (error) {
res.status(404).json({
message: "Something Went wrong",
});
}
Hope this code solve your query and give you desired result. If you still facing issue lemme know.
Thanks

Apollo client not calling with correct variables

I have a query like
export default gql`
query getStatus($statusInput: StatusInput!) {
getStatus(statusInput: $statusInput) {
canAccess
isCorrect
}
}
`;
And then I have a hook that uses this query
const useStatus = () => {
const [someId] = useId();
return useQuery<{ getStatus: StatusResponse }>(getStatus, {
variables: { statusInput: { id: someId, numValue: 1 } },
fetchPolicy: 'no-cache',
skip: !cartId,
ssr: false,
});
};
And I am using it in my component as
const { data: statusData, loading: dataLoading, variables } = useStatus();
In the the component.spec.tsx I have
const mocks = [
{
request: {
query: getStatus,
variables: {
statusInput: {
id: '1234',
numValue: 55,
},
},
},
result: {
data: {
getStatus: {
__type: 'StatusResponse',
canAccess: true,
isCorrect: true
},
}
},
},
];
and inside the test I have
const { queryByTestId, container } = renderWithProviders(
<MockedProvider mocks={mocks} addTypename={true} cache={inMemoryCache}>
<Component />
</MockedProvider>,
{ mockedContextData: someContextMocks }
);
But when I print variables in the component, I get
{ checkoutStatusInput: { id: '', numValue: 1 } }
instead of the values I passed in the mocks.
Did I miss something?

Empty Array is being returned before PromiseAll is resolved when used in useEffect method

I am iterating through a multidimensional array then pushing my data to a new array. When I log my array using the 'then' chain it logs empty. Not sure I am goin about this the proper way.
Array.js
export const Cars = [
{
type: "A",
cars: [
{ name: "buick", id: "1259263" },
{ name: "ford", id: "1299830" },
{ name: "audi", id: "0181545" },
{ name: "gmc", id: "0016024" },
],
},
{
type: "B",
cars: [
{ name: "mazada", id: "1306193" },
{ name: "chevy", id: "1374540" },
{ name: "nissan", id: "1419526" },
{ name: "toyota", id: "1333007" },
],
},
{
type: "C",
cars: [
{ name: "bmw", id: "1259297" },
{ name: "porsche", id: "1305493" },
{ name: "tesla", id: "1042547" },
{ name: "mercedes", id: "1012982" },
],
},
];
CarComponent.js
...
export const CarComponent = () => {
const myArr = [];
useEffect(() => {
const fetchList = () => {
Promise.all(
Cars.map((car) => {
return car.cars.map((id) => {
return new Promise((resolve) => {
fetch(`/api/=${id.id}`).then((response) => {
return new Promise(() => {
response.json().then((id) => {
console.log(id); //<----returns normal
myArr.push(id);
resolve();
});
});
});
});
});
}),
).then(() => {
console.log("myArr", myArr); //<-----array is empty?
})
};
fetchList();
}, []);
...
Look at
Promise.all(
Cars.map((car) => {
return car.cars.map((id) => {
The item being returned from the mapper function is not a Promise, but an array - the return car.cars.map needs to be changed to a Promise.
You should also avoid the explicit Promise construction antipattern.
const fetchList = () => {
Promise.all(
Cars.map(({ cars }) => Promise.all(
cars.map(({ id }) =>
fetch(`/api/=${id}`)
.then(res => res.json())
.then((result) => myArr.push(result))
)
))
).then(() => {
console.log("myArr", myArr);
})
};
Another option, rather than pushing to an external array:
const fetchList = () => {
Promise.all(
Cars.map(({ cars }) => Promise.all(
cars.map(({ id }) =>
fetch(`/api/=${id}`)
.then(res => res.json())
)
))
).then((results) => {
console.log("myArr", results.flat());
})
.catch(handleErrors); // don't forget this part
};

.map is undefined when mapping through the profile.education array

I'm experiencing some difficulty with trying to map through an objects property which is an array of objects. I get back an error message that .map is undefined, basically saying there is no array. The array i want to map through is education. At times I also get a proxy error that the route is timing out and this loses the profile data i'm fetching and the profile object is empty. Is there anyway to fix this too.
My model:
import mongoose from 'mongoose'
const profileSchema = new mongoose.Schema(
{
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
level: {
type: String,
required: true,
enum: [
'None',
'GCSE or equivalent',
'A-Level or equivalent',
'Certificate of Higher Education',
'Diploma of Higher Education',
'Bachelors',
'Masters',
'PhD',
],
},
school: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
city: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
},
],
skills: [
{
name: {
type: String,
required: true,
},
yearsExperience: {
type: Number,
required: true,
},
},
],
additionalInfo: {
desiredJobTitle: {
type: String,
},
desiredJobType: {
type: [String],
},
desiredSalary: {
type: Number,
},
readyToWork: {
type: Boolean,
default: false,
},
relocate: {
type: Boolean,
default: false,
},
},
savedJobs: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Job',
},
],
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
},
{ timestamps: true }
)
const Profile = mongoose.model('Profile', profileSchema)
export default Profile
My action:
import axios from 'axios'
import {
PROFILE_DETAILS_REQUEST,
PROFILE_DETAILS_SUCCESS,
PROFILE_DETAILS_FAIL,
PROFILE_CREATE_REQUEST,
PROFILE_CREATE_SUCCESS,
PROFILE_CREATE_FAIL,
PROFILE_CREATE_EDUCATION_REQUEST,
PROFILE_CREATE_EDUCATION_SUCCESS,
PROFILE_CREATE_EDUCATION_FAIL,
} from '../constants/profileConstants'
import { setAlert } from './alertActions'
export const getCurrentProfile = () => async (dispatch, getState) => {
try {
dispatch({ type: PROFILE_DETAILS_REQUEST })
const {
userLogin: { userData },
} = getState()
const config = {
headers: {
Authorization: `Bearer ${userData.token}`,
},
}
const { data } = await axios.get('/api/v1/profile/me', config)
dispatch({
type: PROFILE_DETAILS_SUCCESS,
payload: data,
})
} catch (error) {
dispatch({
type: PROFILE_DETAILS_FAIL,
payload:
error.response && error.response.data.error
? error.response.data.error
: null,
})
}
}
My reducer:
export const profileDetailsReducer = (state = { profile: {} }, action) => {
switch (action.type) {
case PROFILE_DETAILS_REQUEST:
return {
...state,
loading: true,
}
case PROFILE_DETAILS_SUCCESS:
return {
loading: false,
profile: action.payload,
}
case PROFILE_DETAILS_FAIL:
return {
loading: false,
error: action.payload,
}
case PROFILE_DETAILS_RESET:
return {
profile: {},
}
default:
return state
}
}
My dashboard component:
import React, { useEffect } from 'react'
import Moment from 'react-moment'
import { useDispatch, useSelector } from 'react-redux'
import { getCurrentProfile } from '../actions/profileActions'
import Loader from '../components/layout/Loader'
import DashboardActions from '../components/dashboard/DashboardActions'
const Dashboard = ({ history }) => {
const dispatch = useDispatch()
const profileDetails = useSelector((state) => state.profileDetails)
const { loading, error, profile } = profileDetails
const userLogin = useSelector((state) => state.userLogin)
const { userData } = userLogin
console.log(profile)
useEffect(() => {
if (!userData) {
history.push('/login')
} else {
dispatch(getCurrentProfile())
}
}, [dispatch, history, userData])
return (
<>
<h1 class='mb-4'>Dashboard</h1>
<p>Welcome {userData && userData.name}</p>
<br />
{loading ? (
<Loader />
) : (
<>
<DashboardActions />
<h2 className='my-2'>Education Details</h2>
<table className='table'>
<thead>
<tr>
<th>Level</th>
<th>Field of study</th>
<th>School</th>
</tr>
</thead>
<tbody>{profile.education.map((edu) => console.log(edu))}</tbody>
</table>
</>
)}
</>
)
}
export default Dashboard
Issue
state.profile.educatioin is undefined in your initial state.
export const profileDetailsReducer = (state = { profile: {} }, action) => { ...
Solution(s)
Define an initial state that contains an education array
const initialState = {
profile: {
education: [],
},
};
export const profileDetailsReducer = (state = initialState, action) => { ...
Or provide a fallback value from your selector
const { loading, error, profile: { education = [] } } = profileDetails;
...
<tbody>{education.map((edu) => console.log(edu))}</tbody>
Or provide the fallback in the render
<tbody>{(profile.education ?? []).map((edu) => console.log(edu))}</tbody>

Apollo GraphQL (React) refetchQueries/update after mutation don't update the store

I have a component that behaves like one in Google drive, when you want to move your file/folder. It fetches all the necessary data about directories, displays it, and after one have been chosen - it moves a file into another folder. The goal I'm trying to aim is update data about currently displayed folder and folder where file has been moved. I tried the both way (refetchQueries, update), but non worked...
The main issue is that queries, defined in updateQueries are executed, but store doesn't update.
It would be great, if anyone could help!
const EntityMoveContainer = compose(
graphql(GET_DIRECTORIES, {
options() {/*...*/},
props(props) {/*...*/}
}),
graphql(MOVE_FILE, {
props(props) {
const { mutate, ownProps } = props;
const { entity, directoryId } = ownProps;
return {
async moveFile(destDirId) {
return mutate({
variables: {
fileId: entity.id,
directoryId: destDirId,
},
refetchQueries: () => [
{
query: GET_DIRECTORIES,
variables: {
id: directoryId,
sortKey: store.sortKey,
cursor: store.cursor,
filetype: store.filetype,
},
},
{
query: GET_DIRECTORIES,
variables: {
id: destDirId,
sortKey: store.sortKey,
cursor: store.cursor,
filetype: store.filetype,
},
},
],
/* update(proxy) {
console.log('update method');
try {
const storeData = proxy.readQuery({
query: GET_DIRECTORIES,
variables: {
id: destDirId,
sortKey: store.sortKey,
filetype: store.filetype,
cursor: store.cursor,
},
});
storeData.directory = {
...storeData.directory,
files: {
...storeData.directory.files,
edges: [
...storeData.directory.files.edges,
{ node: entity, __typename: 'File' },
],
},
};
proxy.writeQuery({
query: GET_DIRECTORIES,
variables: {
id: destDirId,
sortKey: store.sortKey,
filetype: store.filetype,
cursor: store.cursor,
},
data: storeData,
});
} catch (e) {
console.log(e);
}
}, */
});
},
};
},
}),
)(EntityMoveView)
The issue was with a
cursor : ''
property I passed into refetchQueries.

Resources