guys, need your help
I have the following question:
Imagine, we have a blog website. We have an author page with lists of posts he created /author/:authorId. And author wants to add another post to the end of this list.
We have a mutation for this:
mutation PostCreateMutation($id: ID!, $title: String!) {
createPost(id: $id, title: $title, text: $text) { … }
}
And after the mutation is called, i want to update UI without refetching all posts. I cannot use options.update method, because my update function looks like this:
update: (proxy, { data: { createPost } }) => {
const data = proxy.readQuery({
query: AUTHOR_QUERY,
variables: {
id: ‘1’ // => i cant get authorId here =(
}
});
}
I cant get authorId in update method, because I don't have an access to component props there… How should i handle this?
In Apollo, options can be a function instead of an object. So the config object you pass to your HOC can look like this:
{
options: ({authorId}) => ({
update: (proxy, { data: { createPost } }) => {
const data = proxy.readQuery({
query: AUTHOR_QUERY,
variables: {
id: authorId,
}
});
}
}),
}
Related
Let's say I have a Post and Comment model. A Post hasMany Comments. In React Apollo I'm using subscribeToMore on a Query for a particular post.
The query appears as follows:
query getPost(id: ID!){
id, title, comments { id }
}
And the subscription which returns the post with any new comments:
subscription commentAdded(postId: ID!){
id, title, comments { id }
}
The query works. It returns all of the associated Comments, which I can then render as in list on the page.
Yet when using the subscribeToMore helper for the query, I get the follow error whenever the event subscription is dispatched.
Cannot read property 'Comment' of undefined.
The strange thing is that if I remove the Comment, such that the subscription looks like...
subscription commentAdded(postId: ID!){
id, title
}
...it works perfectly. I'm confused why it seems to treat the Comments as associating with an undefined model.
This isn't just a Comments -> Posts issue, this happens on any model that tries to return a subscription with an association.
post query:
post: async (parent, {id}, {models}) => {
return await models.Post.findByPk(id);
}
saveComment resolver:
saveComment: async (parent, {postId, comment}, {models, me}) => {
let post = await models.Post.findByPk(postId);
let comment = await models.Comment.create({comment, postId});
await pubsub.publish("COMMENT_CREATED", {
commentCreated: post,
postId
})
}
commentCreated subscription:
commentCreated: {
subscribe: withFilter(
() => pubsub.asyncIterator(["COMMENT_CREATED"]),
(payload, variables) => {
return payload.postId == variables.postId
}
)
}
Post type resolver
Post: {
comments: async (post, args, {models}) => {
return await models.Comment.findAll({where:{postId: post.id}});
}
}
Server initialization:
const server = new ApolloServer({
typeDefs: schema,
resolvers,
subscriptions: {
onConnect: (connectionParams, webSocket) => {
return true
},
},
context: async ({ req, connection }) => {
if(connection){
return connection.context;
}else{
const me = await getMe(req);
return {
models,
me,
secret: process.env.SECRET,
};
}
}
});
Your context function only returns connection.context, which will not include any of the custom properties you want to include (me, models, etc.). Doing something like this should fix the problem:
context: async ({ req, connection }) => {
const context = {
models,
secret: process.env.SECRET,
};
if(connection){
return { ...connection.context, ...context };
} else {
const me = await getMe(req);
return { ...context, me };
}
}
I currently have the mutation hook set up as following:
const bookItem = useMutation(CREATE_BOOKING_MUTATION, {
variables: mutationVariables,
refetchQueries: [{ query: getPosts }]
})
The refetchQueries property is set in place, but console.logging bookItem does not give me access to the returned data.
Edit:
#xadm
I wanted to access the retrieved data from the delete mutation as well using the update property as you mentioned, but still had difficulty accessing the returned info.
const onDeleteHandler = useMutation(DELETE_POST, {
update: (proxy, mutationResult) => {
try {
const { deletePost } = mutationResult.data;
const postFeed = proxy.readQuery({
query: GET_POSTS, variables
})
const data = _.omit(postFeed, [deletePost.id])
proxy.writeQuery({ query: GET_POSTS, variables, data })
}
catch(error){
console.log("Cache update error", error)
}
},
})
Hello guys I am new in this Apollo - GraphQL and I am using I try to implement my server with my React Native app.
While I am trying to build the Password Change functions I get the following error this.props.resetPassword is not a function. (In 'this.props.resetPassword(_id, password)', 'this.props.resetPassword' is undefined)
My code looks like this
toSend() {
const { _id } = this.props.data.me;
const { password } = this.state;
console.log(_id, password)
this.props
.resetPassword(_id, password)
.then(({ data }) => {
return console.log(data);
})
}
And here is my query and my mutation
export default graphql(
gql`
query me {
me {
_id
}
}
`,
gql`
mutation resetPassword($_id: String!, $password: String!) {
resetPassword(_id: $_id, password: $password) {
_id
}
}
`,
{
props: ({ mutate }) => ({
resetPassword: (_id, password) => mutate({ variables: { _id, password } }),
}),
},
)(PasswordChange);
If you're using the graphql HOC, you'll need to use compose to combine multiple queries/mutations. For example:
const mutationConfig = {
props: ({mutate, ownProps}) => ({
resetPassword: (_id, password) => mutate({ variables: { _id, password } }),
...ownProps,
})
}
export default compose(
graphql(YOUR_QUERY, { name: 'createTodo' }),
graphql(YOUR_MUTATION, mutationConfig),
)(MyComponent);
Keep in mind that the HOC will pass down whatever props it receives by default, but if you provide a function for props in the configuration, it's on you to do so by utilizing ownProps. Also, when using compose, the order of the HOCs matters if any of them are dependent on the others for props. That means as long as your query comes first inside compose, you can use the query HOC's data inside your mutation's configuration.
I have test app where I'm subscribing to a list of todos. But my add new todo mutation also optimistically updates the UI.
The problem is it's now refreshing the entire react view and I have no idea why.
This is the culprit, it happens when I run this mutation:
export default graphql(addItem, {
options: {
fetchPolicy: 'cache-and-network'
},
props: props => ({
onAdd: item =>
props.mutate({
variables: item,
optimisticResponse: {
__typename: 'Mutation',
addItem: { ...item, __typename: 'Item' }
},
update: (proxy, { data: { addItem } }) => {
let data = proxy.readQuery({ query: listItems });
data.listItems.items.push(addItem);
proxy.writeQuery({ query: listItems, data });
}
})
})
})(AddItem);
It turns out that mutating items locally without all the queried fields (in my case "created" is added by the server) causes the entire query mutation to fail but silently. There's a fix on the way: https://github.com/apollographql/apollo-client/issues/3267
Hopefully this helps others starting out!
In one of my components within a redux-form onSubmit, I have the following:
const result = await helloSignup(values);
console.log(result);
helloSignup is mutating the database as expected but the const result is currently be logged as undefined
Why?
My HOC/mutation helloSignup:
export const HELLO_SIGNUP_MUTATION = gql`
mutation (
$email: String!
$code: String!
) {
signup(authProvider: {
emailAndCode: {
email: $email
code: $code
}
}) {
token
user {
id
}
}
}
`;
export default graphql(
HELLO_SIGNUP_MUTATION,
{
props: ({ mutate }) => ({
emailAndCodeSignup: async (variables) => {
const { data } = await mutate({ variables });
const { token } = data.signup;
},
}),
}
);
Using GraphiQL, I can see that my graphql mutation, returns the desired results:
{
"data": {
"signup": {
"token": "xxx",
"user": {
"id": "16"
}
}
}
}
If GraphiQL is getting the desired results after mutating, why isn't the result being console logged above?
React-Apollo provides a HOC for client side queries and mutations called withApollo.
This signature is something like this:
withApollo(MyForm)
https://www.apollographql.com/docs/react/basics/setup.html#withApollo
which adds a prop of 'client' to the MyForm component. On form submission, you'd want to access this prop, and call the mutation from there. So in your form submit handler youd end up with something like this:
https://www.apollographql.com/docs/react/basics/mutations.html#basics
onSubmit() {
const { client } = this.props
const options = {} // your mutation options
// mutations ands queries return promises,
// so you must wait for their completion before accessing data
client.mutate(
HELLO_SIGNUP_MUTATION,
options
).then(({ data }) => (
console.log('got data', data);
)
}
}
Where data should be whats coming back from the API