React GraphQL mutation returning Invalid input - reactjs

Background: I'm building a Shopify app using React, NextJS, and GraphQL. The functionality is to add an extra privateMetafield with a value for each selected product.
The problem: I'm trying to create or update (if privateMetafield exists) with Mutation using React-Apollo. I have tried to run the GraphQL mutation in Insomnia (like postman for GraphQL) and it works. But when I add it to my code I don't get the GraphQL to receive the mutation data. Instead, I get this error message:
Unhandled Runtime Error
Error: GraphQL error: Variable $input of type PrivateMetafieldInput! was provided invalid value for privateMetafields (Field is not defined on PrivateMetafieldInput), namespace (Expected value to not be null), key (Expected value to not be null), valueInput (Expected value to not be null)
Insomnia Successful GraphQL test (what it should be like)
edit-products.js
...
import gql from 'graphql-tag';
import { Mutation } from 'react-apollo';
...
const UPDATE_EMISSION = gql`
mutation($input: PrivateMetafieldInput!) {
privateMetafieldUpsert(input: $input) {
privateMetafield {
namespace
key
value
}
}
}
`;
...
class EditEmission extends React.Component {
state = {
emission: '',
id: '',
showToast: false,
};
render() {
const { name, emission, id } = this.state;
return (
<Mutation
mutation={UPDATE_EMISSION}
>
{(handleSubmit, { error, data }) => {
const showError = error && (
<Banner status="critical">{error.message}</Banner>
);
const showToast = data && data.productUpdate && (
<Toast
content="Sucessfully updated"
onDismiss={() => this.setState({ showToast: false })}
/>
);
return (
<Frame>
... <Form>
<TextField
prefix="kg"
value={emission}
onChange={this.handleChange('emission')}
label="Set New Co2 Emission"
type="emission"
/>
...
<PageActions
primaryAction={[
{
content: 'Save',
onAction: () => {
const item = store.get('item');
const id = item.id;
const emissionVariableInput = {
owner: id,
privateMetafields: [
{
namespace: "Emission Co2",
key: "Co2",
valueInput: {
value: emission,
valueType: "INTEGER"
}
}
]
};
console.log(emissionVariableInput)
handleSubmit({
variables: { input: emissionVariableInput },
});
}
}
]}
secondaryActions={[
{
content: 'Remove emission'
}
]}
/>
</Form>
...
</Mutation>
);
}
handleChange = (field) => {
return (value) => this.setState({ [field]: value });
};
}
export default EditEmission;
I get this in the console when I log emissionVariableInput, which looks correct. But why is the data not passed properly to the GraphQL mutation and how do I fix it?
I expect the GraphQL mutation to be successful and create/update a privateMetafield owned by a product.
Thanks in advance!

You have different shapes for input in your examples. In Insomnia you pass:
{
owner: id,
namespace: "Emission Co2",
key: "Co2",
valueInput: {
value: emission,
valueType: "INTEGER"
}
}
While in the code your input looks like:
{
owner: id,
privateMetafields: [{
namespace: "Emission Co2",
key: "Co2",
valueInput: {
value: emission,
valueType: "INTEGER"
}
}]
};

Related

How to submit a react form with chakra react select (v4), react hook form and prisma

I am trying to figure out how to make a form in a react, apollo, prisma app, using chakra-react-select.
This is my attempt:
import * as React from "react"
import { Box, Center, Heading, Button } from "#chakra-ui/react"
import { Select } from "chakra-react-select"
import { gql } from "#apollo/client"
import Head from "next/head"
import { IssueInput, useAllIssuesQuery, useCreateIssueMutation } from "lib/graphql"
import { useRouter } from "next/router"
import { REDIRECT_PATH } from "lib/config"
import * as c from "#chakra-ui/react"
import { Input } from "components/Input"
import { HomeLayout } from "components/HomeLayout"
import { Limiter } from "components/Limiter"
import { Form } from "components/Form"
import { withAuth } from "components/hoc/withAuth"
import Yup from "lib/yup"
import { useForm } from "lib/hooks/useForm"
import { useMe } from "lib/hooks/useMe"
import { useToast } from "lib/hooks/useToast"
import { useController } from "react-hook-form"
const _ = gql`
mutation CreateIssue($data: IssueInput!) {
createIssue(data: $data) {
id
title
issueCategory
description
}
# userId
}
query AllIssues {
allIssues {
id
title
# issueId
description
issueCategory
userId
}
}
`
const IssueSchema = Yup.object().shape({
title: Yup.string().required("Title is required"),
issueCategory: Yup.object().required(),
description: Yup.string().required("Description is required"),
})
function Issue() {
const toast = useToast()
const { me, loading: meLoading } = useMe()
const [createIssue] = useCreateIssueMutation()
const { data: issues, refetch } = useAllIssuesQuery()
const router = useRouter()
const redirect = router.query[REDIRECT_PATH] as string | undefined
const form = useForm({ schema: IssueSchema })
const {
field: { onChange, onBlur, value, ref },
fieldState: { error },
} = useController({
name: "issueCategory",
control: form.control,
rules: { required: "Issue category is required" },
})
const onSubmit = (data: IssueInput) => {
console.log("hello onsubmit", data) /// I can see this in my console when I try to submit this form but instead of storing the data it says undefined
return form.handler(
() =>
createIssue({
variables: {
data: {
...data,
// #ts-ignore
issueCategory: data.issueCategory.value,
userId: me?.id || "",
},
},
}),
{
onSuccess: async (data) => {
console.log(data)
await fetch("api/issue", { method: "post" }),
toast({
title: "Issue created",
description: "Your issue has been created",
status: "success",
})
refetch()
form.reset()
router.replace(redirect || "/")
},
},
)
}
if (meLoading)
return (
<c.Center>
<c.Spinner />
</c.Center>
)
if (!me) return null
console.log("select error", error)
return (
<Box>
<Head>
<title>Create</title>
</Head>
<Limiter pt={20} minH="calc(100vh - 65px)">
<Center flexDir="column">
<Heading
as="h1"
size="3xl"
>
Create Issue
</Heading>
<Form onSubmit={onSubmit} {...form}>
<c.Stack spacing={2}>
<c.Heading>Issues</c.Heading>
<Input autoFocus name="title" label="Title" placeholder="Eg: change" />
<Input
name="description"
label="Description"
placeholder="Eg: change"
/>
<Select
options={[
{
label: "I can't be removed",
value: "fixed",
isFixed: true,
},
{
label: "I can be removed",
value: "not-fixed",
},
]}
placeholder="Select issue category"
closeMenuOnSelect={true}
onChange={onChange}
onBlur={onBlur}
value={value}
ref={ref}
/>
<p>{error?.message}</p>
<Button
color="brand.orange"
type="submit"
// isFullWidth
isDisabled={form.formState.isSubmitting || !form.formState.isDirty}
isLoading={form.formState.isSubmitting}
>
Create Issue
</Button>
<c.List>
{issues?.allIssues.map((issue) => (
<c.ListItem key={issue.id}>
{issue.title}
{/* {issue.issueCategory} */} - if I uncomment this line, then i get an error as described below
{issue.description}
</c.ListItem>
))}
</c.List>
</c.Stack>
</Form>
</Center>
</Limiter>
</Box>
)
}
Issue.getLayout = (page: React.ReactNode) => <HomeLayout>{page}</HomeLayout>
export default withAuth(Issue)
I can't understand why my terminal is giving me an error message that says that the property named issueCategory does not exist on type Issue. I can see that a type has been generated for IssueCategory:
export type Issue = {
__typename?: 'Issue';
createdAt: Scalars['DateTime'];
description: Scalars['String'];
id: Scalars['String'];
issueCategory: Category;
title: Scalars['String'];
updatedAt: Scalars['DateTime'];
userId: Scalars['String'];
};
My prisma schema also has an attribute for issueCategory, as follows:
model Issue {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issueCategory Category
// Template Template[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
User User[]
userId String #db.Uuid
}
Category is also defined in graphql.tsx as:
export enum Category {
Future = 'FUTURE',
Natural = 'NATURAL',
Social = 'SOCIAL'
}
Apart from this strange error message, I don't have any other errors in my terminal. I don't get any information about the error in my console either - it just says 'select error undefined'. I can see in the entry above the error line, that the json packet includes the selection i made when the form loads.
What is the step required to submit this form? Currently my console logs that the data submitted is undefined.

Why does my child component render multiple times?

I am using Stripe API on my project and when the payment is successful I want to add the order placed to my collection. When I run the below code it adds the order multiple times and there is no specific pattern. It also adds multiple orders to the collection:
Success.js:
import React from "react";
import AddOrder from "../orders/AddOrder";
import { useNavigate, useParams } from "react-router-dom";
import { useMutation, useQuery } from "#apollo/client";
import queries from "../../queries";
import { Alert, Button } from "react-bootstrap";
function Success() {
const logo = require("../../assets/delivery-package.gif");
const navigate = useNavigate();
const { secret } = useParams();
const { loading, error, data } = useQuery(queries.GET_SESSION, { variables: { id: secret } });
const [deleteSession] = useMutation(queries.DELETE_SESSION);
if (loading) {
return <div>Loading...</div>;
} else if (error) {
navigate("/notfound");
} else if (data.session) {
deleteSession({
variables: {
id: secret,
},
});
return (
<div>
<AddOrder />
<Alert variant="success" style={{ fontSize: "25px" }}>
Order Placed Successfully
</Alert>
<img alt="order success" id="logo" src={logo} style={{ width: "70%", height: "70%", marginTop: "30px" }} />
<br />
<br />
<Button onClick={() => navigate("/")}>Home</Button>
</div>
);
} else {
navigate("/notfound");
}
}
export default Success;
AddOrder:
import { useMutation, useQuery } from "#apollo/client";
import { AuthContext } from "../../Firebase/Auth";
import queries from "../../queries";
import { useContext, useEffect } from "react";
import { reactLocalStorage } from "reactjs-localstorage";
let add = reactLocalStorage.getObject("addressDetails");
function AddOrder() {
const d = new Date();
let text = d.toString();
const [addOrder] = useMutation(queries.ADD_ORDER);
const [editUser] = useMutation(queries.EDIT_USER_CART);
const { currentUser } = useContext(AuthContext);
const { data } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "network-only",
variables: {
id: currentUser.uid,
},
});
const getUserOrders = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "network-only",
variables: {
userId: currentUser.uid,
},
});
useEffect(() => {
if (data && getUserOrders.data && currentUser && data.getUser.cart.length > 0) {
let newCart = [];
let total = 0;
for (let i = 0; i < data.getUser.cart.length; i++) {
total += data.getUser.cart[i].price * data.getUser.cart[i].quantity;
newCart.push({
orderedQuantity: data.getUser.cart[i].quantity,
_id: data.getUser.cart[i]._id,
name: data.getUser.cart[i].name,
image: data.getUser.cart[i].image,
price: data.getUser.cart[i].price,
});
}
addOrder({
variables: {
userId: currentUser.uid,
userEmail: currentUser.email,
status: "ordered",
createdAt: text,
products: newCart,
total: total,
flag: getUserOrders.data.userOrders.length + 1,
zip: add.zip.val ? add.zip.val : add.zip,
state: add.state.val ? add.state.val : add.state,
city: add.city.val ? add.city.val : add.city,
apt: add.apt.val ? add.apt.val : add.apt,
addressStreet: add.addressStreet.val ? add.addressStreet.val : add.addressStreet,
},
});
editUser({
variables: {
id: currentUser.uid,
cart: [],
},
});
}
}, [addOrder, currentUser, data, editUser, getUserOrders, text]);
}
export default AddOrder;
There is only one return so don't know if the issue is with the success page or the Addorder function
Possible reason:
Your component must be being rendered by some parent component, for example, by calls to setState, and when the component is re-rendered the Success child component (which in turn does not return a JSX.Element) is also re-rendered
Cause:
Inside your AddOrder component there is a useEffect that serves to prevent re-renders when the passed parameters were not changed, and among them it has a "new Date().getTime()", I believe this is one of the main reasons why useEffect is always returning true, and requesting a re-render. Not only that, but also:
new Date().toString as parameter to useEffect
Passing functions (addOrder, editOrder) to useEffect params that are likely to always be new functions when reredendering.
currentUser is probably a new object every render, luckily you only need the userid, which in turn is a primitive type, and will always be the same regardless of re-rendering.
Solution:
There is no need to analyze if the addOrder or editOrder function has changed, as they are just functions, and do not add any specific "value", for example, if your intention is to rederize (to be able to add) only when the items change, you can just leave the getUserOrders.data and data (useQuery(queries.GET_USER_BY_ID)...)
New code:
import { useMutation, useQuery } from "#apollo/client";
import { AuthContext } from "../../Firebase/Auth";
import queries from "../../queries";
import { useContext, useEffect } from "react";
import { reactLocalStorage } from "reactjs-localstorage";
let add = reactLocalStorage.getObject("addressDetails");
function AddOrder() {
const d = new Date();
let text = d.toString();
const [addOrder] = useMutation(queries.ADD_ORDER);
const [editUser] = useMutation(queries.EDIT_USER_CART);
const { currentUser } = useContext(AuthContext);
const { data } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "network-only",
variables: {
id: currentUser.uid,
},
});
const getUserOrders = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "network-only",
variables: {
userId: currentUser.uid,
},
});
useEffect(() => {
if (data && getUserOrders.data && currentUser && data.getUser.cart.length > 0) {
let newCart = [];
let total = 0;
for (let i = 0; i < data.getUser.cart.length; i++) {
total += data.getUser.cart[i].price * data.getUser.cart[i].quantity;
newCart.push({
orderedQuantity: data.getUser.cart[i].quantity,
_id: data.getUser.cart[i]._id,
name: data.getUser.cart[i].name,
image: data.getUser.cart[i].image,
price: data.getUser.cart[i].price,
});
}
addOrder({
variables: {
userId: currentUser.uid,
userEmail: currentUser.email,
status: "ordered",
createdAt: text,
products: newCart,
total: total,
flag: getUserOrders.data.userOrders.length + 1,
zip: add.zip.val ? add.zip.val : add.zip,
state: add.state.val ? add.state.val : add.state,
city: add.city.val ? add.city.val : add.city,
apt: add.apt.val ? add.apt.val : add.apt,
addressStreet: add.addressStreet.val ? add.addressStreet.val : add.addressStreet,
},
});
editUser({
variables: {
id: currentUser.uid,
cart: [],
},
});
}
}, [currentUser.uid, data, getUserOrders.data]);
}
export default AddOrder;
Please check if the code works, anything please comment so I can correct my answer (I haven't tested it yet, but I believe it will work).
Although it is not recommended, as the good practice recommends the use of memos, you can try in the last case (only to verify if this is in fact the problem) to use as follows:
[currentUser.uid, JSON.stringify(data), JSON.stringify(getUserOrders.data)]
In AddOrder component, you are not returning any JSX. You are actually sending order request in the body of the component ( during rendering, which is super bad since it is a side-effect).
Don't return addOrder, editUser calls. Place them inside of useEffect or event handlers depending on your business logic.

Checkbox for specific row in react-table?

import React, { Component } from 'react';
import { connect } from 'react-redux';
import getSchoolsList from '../Actions/Index';
import ReactTable from "react-table";
import checkboxHOC from "react-table/lib/hoc/selectTable";
import "react-table/react-table.css";
const CheckboxTable = checkboxHOC(ReactTable);
class Home extends Component {
constructor(props){
super(props);
this.state = {
selection: [],
selectAll: false
};
}
componentDidMount(){
this.props.getSchoolsList();
}
toggleSelection = (key, shift, row) => {
let selection = [...this.state.selection];
const keyIndex = selection.indexOf(key);
if (keyIndex >= 0) {
selection = [
...selection.slice(0, keyIndex),
...selection.slice(keyIndex + 1)
];
} else {
selection.push(key);
}
this.setState({ selection });
};
toggleAll = () => {
const selectAll = this.state.selectAll ? false : true;
const selection = [];
if (selectAll) {
const wrappedInstance = this.checkboxTable.getWrappedInstance();
const currentRecords = wrappedInstance.getResolvedState().sortedData;
currentRecords.forEach(item => {
selection.push(item._original._id);
});
}
this.setState({ selectAll, selection });
};
isSelected = key => {
console.log(key);
return this.state.selection.includes(key);
};
logSelection = () => {
console.log("selection:", this.state.selection);
};
render() {
const { toggleSelection, toggleAll, isSelected, logSelection } = this;
const { selectAll } = this.state;
const checkboxProps = {
selectAll,
isSelected,
toggleSelection,
toggleAll,
selectType: "checkbox",
};
const data = this.props.StateData?this.props.StateData.data:[];
const {loading, StateData} = this.props;
if (loading) {
{console.log(loading)}
return <div>Loading...</div>;
}
return (
<div>
{console.log(this.checkboxTable)}
<button onClick={logSelection}>Log Selection</button>
<CheckboxTable
ref={r => (this.checkboxTable = r)}
data={data}
columns={[
{
Header: "School Name",
accessor: "name"
},
{
Header: "Location",
id: "lastName",
accessor: d => d.area + ',' + d.city
},
{
Header: "Curriculum",
accessor: "curriculum"
},
{
Header: "Grade",
accessor:"grade"
},
{
Header: "Web App_URL",
accessor: "webapp_url",
},
{
Header: "Status",
id: "status",
accessor: d =>{
if(d.publish === true){
console.log(d.publish)
return 'Publish';
}else{
return 'Unpublished'
}
}
}
]}
defaultPageSize={10}
className="-striped -highlight"
{...checkboxProps}
/>
</div>
);
}
}
function mapStateToProps (state) {
return {
StateData:state.login.schools,
loading: state.login.loading,
}
};
export default connect(mapStateToProps, {getSchoolsList})(Home);
Hi all, can someone help me with this what is the wrong i am not getting individual checkboxes in this ? i checked this link code in my local it is working <https://codesandbox.io/s/7yq5ylw09j?from-embed>, but whenever i add my dynamic data it is not working.
Hi all, can someone help me with this what is the wrong i am not getting individual checkboxes in this ? i checked this link code in my local it is working <https://codesandbox.io/s/7yq5ylw09j?from-embed>, but whenever i add my dynamic data it is not working.
Hi all, can someone help me with this what is the wrong i am not getting individual checkboxes in this ? i checked this link code in my local it is working https://codesandbox.io/s/7yq5ylw09j?from-embed, but whenever i add my dynamic data it is not working.
If your using TypeScript and tslint this happens via the example for select table(checkboxes) getdata() does this:
const _id = chance.guid();
return {
_id,
...item
};
tslint complains about the _id var naming with "variable name must be in lowerCamelCase, PascalCase or UPPER_CASE"
You can see that at: https://react-table.js.org/#/story/select-table-hoc
So you have to change _id to id if you want to get past tslint. Changing from _id to id breaks the default keyField logic in react-table which wants _id. That necessitates setting the keyField property to "id".
If you do not mention unique key id by default it will take "_id" as the key field. By defining a key value you can overcome the above mentioned matter as follows.
Let's say there is a specific column named "USER ID". And we'll take the accessor of the column as "uid".
The code should be modified as follows.
Checkbox Table
<CheckboxTable
keyField="uid"
......Rest of your code....
/>
toggleAll()
toggleAll() {
..........code...........
currentRecords.forEach(item => {
selection.push(item.uid);
});
}
.......code............
}

React, Apollo - Update & Remove Mutation Issues

I am currently working and study on "CRUD" mutation with GraphQL. Using Apollo Client and React to control mutation at front-end. Using Express/mongoose as back-end to run server and fetch/store data from local database.
Working/Test with GraphQL (Middle/Back-end). I am able to fetch and mutate (create, update, and remove) data from local server (mongodb) through localhost:4000/graphiql tool.
Here's mutation schema...
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addUser: {
...
},
updateUser: {
type: UserType,
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
name: { type: new GraphQLNonNull(GraphQLString) },
age: { type: new GraphQLNonNull(GraphQLInt) }
},
resolve(parent, args) {
let updateIser = User.findByIdAndUpdate(
args.id,
{
$set: { name: args.name, age: args.age }
},
{ new: true }
).catch(err => new Error(err));
return updateUser;
}
},
deleteUser: {
type: UserType,
args: { id: { type: new GraphQLNonNull(GraphQLString) } },
resolve(parent, args) {
let removeUser = User.findByIdAndRemove(args.id).exec();
if (!removeUser) {
throw new Error('Error, Cannot Delete!');
}
return removeUser;
}
}
}
});
Now on the the Front-End part using React and Apollo Client, fetching data and rendering iteration map was a success!
I also use react-router for page transition, When clicking on Add button from user list, it will bring me to form page to fill in user name and age. After complete, it will Submit and redirect (react-router redirect method) back to list page with a newly created user into list (refetch). This process was also a success for me!
On the Update (Edit) or Remove (Delete) part, It was unfortunate. I am facing several issues...Unable to update (Edit) user
I am guessing... It appear having something to do with not be able to fetch specific ID after clicking on Edit Button, or could it be some conflicting issue between fetching and react-router? As much as I have tried to find solutions, I ran out of ideas. So I was hoping I could use some help from you to solve on what I am facing.
If this helps... Here's React-Apollo syntax called List.js - This display the list of users including Edit and Remove button (unsuccessful attempts)
import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import { Link } from 'react-router-dom';
import { getUsersQuery } from '../queries/queries';
class UserList extends Component {
state = { selected: null };
displayUser = () => {
let data = this.props.data;
if (data.loading) {
return <div>Loading...</div>;
} else {
return data.examples.map(user => (
<li
key={user.id}
onClick={e => { this.setState({ selected: user.id });}}
>
{user.name} {user.age}
<div className="choices">
<Link
to="/updateuser"
onClick={e => {
this.setState({ selected: user.id });
}}
>
<button>Edit</button>
</Link>
<Link to="/removeuser">
<button>Remove</button>
</Link>
</div>
</li>
));
}
};
render() {
console.log(this.state.selected);
return (
<div id="listContent">
<h3>List</h3>
<ul id="userList"> {this.displayUser()}</ul>
<Link to="/adduser">
<button>+</button>
</Link>
<div>{this.state.selected}</div>
</div>
);
}
}
export default graphql(getUsersQuery)(UserList);
The UpdateForm.js - A syntax that render update form. But couldn't succeed mutate it. possibility fetching issues?
import React, { Component } from 'react';
import { graphql, compose } from 'react-apollo';
import { Redirect, Link } from 'react-router-dom';
import { getUsersQuery, updateUserMutation } from '../queries/queries';
class UpdateUserForm extends Component {
state = {
name: '',
age: '',
redirect: false
};
onChangeName = e => {
this.setState({ name: e.target.value });
};
onChangeAge = e => {
this.setState({ age: e.target.value });
};
onSubmitForm = e => {
e.preventDefault();
//console.log(this.state);
this.props.updateUserMutation({
variables: {
id: this.state.id,
name: this.state.name,
age: this.state.age
},
refetchQueries: [ { query: getUsersQuery } ]
});
this.setState({ redirect: true });
};
render() {
const user = this.props.getUsersQuery;
if (this.state.redirect) {
return <Redirect to="/" />;
}
return (
<div id="formContainer">
<form id="form" onSubmit={this.onSubmitForm}>
<div className="field">
<label>Name:</label>
<input
type="text"
onChange={this.onChangeName}
placeholder={user.name}
/>
</div>
<div className="field">
<label>Age:</label>
<input
type="text"
onChange={this.onChangeAge}
placeholder={user.age}
/>
</div>
<button>Update</button>
<Link to="/">Cancel</Link>
</form>
</div>
);
}
}
export default compose(
graphql(getUsersQuery, { name: 'getUsersQuery' }),
graphql(updateUserMutation, { name: 'updateUserMutation' })
)(UpdateUserForm);
Not sure about remove page -- Hoping just simply hit remove button, from list page will fetch specific ID and mutate (remove) user.
Here's the Apollo Client query code called queries.js.
// Fetch Users
const getUsersQuery = gql`
{
users {
name
age
id
}
}
`;
// Update User Mutation
const updateUserMutation = gql`
mutation($id: ID!, $name: String!, $age: Int!) {
updateUser(id: $id, name: $name, age: $age) {
id
name
age
}
}
`;
// Remove User Mutation
const removeUserMutation = gql`
mutation($id: ID!) {
deleteUser(id: $id) {
id
}
}
`;
Thanks in advance...

No Variables in Relay generated mutation query

I'm pretty new to Relay so perhaps it's a very stupid error.
I'm trying to make a simple mutation that add a defect to a photo.
Here is my Relay.Mutation object :
AddDefectMutation.js
export default class AddDefectMutation extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation { addDefect }`;
}
getVariables() {
return{
photoId: this.props.photoId,
title: this.props.title
}
}
getFatQuery() {
return Relay.QL`
fragment on AddDefectMutationPayload {
updatedPhoto {
issues
}
}
`
}
getConfigs() {
return [{
type : 'FIELDS_CHANGE',
fieldIDs : {
updatedPhoto : this.props.photoId
}
}]
}
}
And here is the part of the GraphQl schema
const AddDefectMutation = mutationWithClientMutationId({
name: 'AddDefectMutation',
description: 'Add a new defect and return all the defects.',
inputFields: {
photoId: {
description: 'Photo of this defect',
type: new GraphQLNonNull(GraphQLString)
},
title: {
description: 'A short description of the defect',
type: GraphQLString
}
},
outputFields: {
updatedPhoto: {
type: PhotoGraphQLType,
resolve: ({localIdIssue}) => driver.getIssuePhoto(localIdIssue)
}
},
mutateAndGetPayload: ({photoId, title}) =>
driver.addIssue(photoId, title).then(localIdIssue => ({localIdIssue}))
})
const MutationGraphQLType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
addDefect: AddDefectMutation
})
})
My problem is that when I make this call :
Relay.Store.commitUpdate(new AddDefectMutation(
{photoId: this.props.pictureId, title: this.props.title}), {
onSuccess: ()=> console.log("Mutation Success !"),
onFailure: transaction => console.error(transaction.getError() || new Error('Mutation failed.'))
})
Relay generate the good mutation query without problem but it doesn't place the variables given in the constructor.
EDIT : Here the fragment of mutation generated by relay
mutation AddDefect($input_0:AddDefectMutationInput!) {
addDefect(input:$input_0) {
...F4,
clientMutationId
}
}
And the problem is that $input_0 is an empty object
The variable title is not passed properly to the mutation constructor. In your Relay.Store.commitUpdate function call, change {photoId: this.props.pictureId, this.props.title}) to
{photoId: this.props.pictureId, title: this.props.title})

Resources