I am very new to relay and GraphQL and looking to emulate the behaviour of after adding a mutation, my component updates with the new mutation. For example, when I add an animal, it then shows up on my animal list.
I have read a bit about manually updating the store via optimistic updates, fetch container etc, however wondering if this behaviour can occur by using just a QueryRenderer HOC and a simple mutation.
The mutation works successfully, however I can only see the update after I refresh that page.
This is the code for the root query render:
export const Customer = ({ userId }) => {
return (
<QueryRenderer
environment={environment}
query={query}
variables={{ userId }}
render={({ error, props }) => {
//handle errors and loading
const user = props.customers_connection.edges[0].node
return (
<div className="flex justify-between items-center">
<h1 className="text-xl">Pets</h1>
<AddPetForm />
<PetList user={user} />
</div>
)
}}
/>
)
}
const query = graphql`
query CustomerQuery($userId: bigint) {
customers_connection(where: { userId: { _eq: $userId } }) {
edges {
node {
id
userId
pets {
id
animal {
...Animal_animal
}
}
}
}
}
}
`
Here is the Animal
component that lives in the PetList
component. The PetList
component is the one I am expect to re-render, to include a new Animal
component with the animal created with the mutation.
const Animal = ({ animal }) => {
return (
<li>
<Summary animal={animal} />
</li>
)
}
export default createFragmentContainer(Animal, {
animal: graphql`
fragment Animal_animal on animals {
name
category
imageUrl
weight
type
}
`
})
Here is the AddPetForm
component:
const AddPetForm = () => {
const { hide } = useModal()
return (
<Modal>
<QueryRenderer
environment={environment}
query={query}
variables={{}}
render={({ error, props }) => {
//handle errors and loading
return (
<Form
hide={hide}
//pass down props from query
/>
)
}}
/>
</Modal>
)
}
const query = graphql`
query AddPetFormQuery {
enum_animal_category_connection {
edges {
node {
id
value
}
}
}
//other enums to use in the form
}
`
And the code for the mutation (this lives in the AddPetForm
component):
const Form = ({ hide, ...other props }) => {
const { handleSubmit, register, errors, } = useForm({ defaultValues, resolver })
const [mutationIsInFlight, setMutationIsInFlight] = useState(false)
const [mutationHasError, setMutationHasError] = useState(false)
const onCompleted = (response, error) => {
setMutationIsInFlight(false)
if (error) onError(error)
else showSuccessNotification()
}
const onError = error => {
setMutationIsInFlight(false)
setMutationHasError(true)
}
const onSubmit = animal => {
setMutationIsInFlight(true)
setMutationHasError(false)
//assume animal has already been added
addAnimalAsPet({ animalId: animal.animalId, userId, onCompleted, onError })
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-wrap">
// all of the inputs
<div className="mt-3 w-full">
<PrimaryButton type="submit" fullWidth={true} loading={mutationIsInFlight}>
Save
</PrimaryButton>
</div>
{mutationHasError && <p className="text-red-700 my-3">An error has occurred. Please try again.</p>}
</form>
)
}
And the mutation code:
const mutation = graphql`
mutation addAnimalAsPet Mutation($animalId: Int, $userId: Int) {
insert_pets_one(object: { animalId: $animalId, userId: $userId }) {
id
}
}
`
export const addAnimalAsPet = ({ userId, animalId, onCompleted, onError }) => {
const variables = { userId, animalId }
commitMutation(environment, {
mutation,
variables,
onCompleted,
onError
})
}
Is there something obvious I am doing wrong? As you can see, there is a nested QueryRenderer HOC, is this breaking the automatic update somehow?
I have not used a subscription as I do not need real time event listening. This data would only be updated when the user adds a new pet, and this doesn't occur very frequently. And I haven't used the optimistic updates/response as there isn't really a need to show the user the update is successful before waiting for the response.
From the docs it looks like there is a way to do this using a Refetch Container, however will have to refactor my code to use a fragment.
The suggestion by xadm worked like a charm. There is a
retry
function that is a property of the object in theQueryRenderer
render
prop.Here is a working example:
I then simply call the
refreshCustomerQuery
function just after a successful mutation.