Intended outcome:
We want to use the Mutation component, but conditionally execute on two Mutations (dependent on a prop received by the component it wraps).
I can't find documentation or use-cases that demonstrate this. Would this be a better use case for a HOC using graphql? or should I have a totally separate mutation function (that does A + B) rather than have one mutation that does A and maybe B?
Actual outcome:
<Mutation
mutation={createMutation}
>
{(createNode) => {
const maybeCreateNewNodes = (propData) =>
createNode({
variables: {
nodes: [newFolderNode],
options,
},
});
if (propData.specialProp) {
doSecondMutation({ variables: { specialProp }})
}
};
return (
<NewtemContainer
twoMutations={maybeCreateNewNodes}
{...props}
/>
);
}}
</Mutation>
Stacking Mutation components doesn't seem to be a great use-case if I can just do
yield call (firstMutation)
if (specialProp)
yield call(secondMutation)
Version
Multiple mutations seems to have been part of previous versions and also in previous docs. E.g. in the discussion of #238, frehner is even linking to http://dev.apollodata.com/react/mutations.html#multiple-mutations that seems to have been a full section about handling multiple mutations in the docs. In the current docs there is no mentions about this. Seems unfair :)
I'm also interested in something very close to this. I have a table with items and the user can perform CRUD operations on any of the items. I was thinking it might be nice to pass in an object instead of just a single mutation query.
Something like:
<Mutation mutations={ addItem: ADD_ITEM, deleteItem: DELETE_ITEM }>
{({ addItem, deleteItem }) => { ... }
</Mutation>
My approach to this has been to use a combination of the graphql HOC referencing the mutation with props inside of the Mutation component. This works, but it would be much nicer to have the option of an object in the 'mutations' property listing multiple mutations has @brennancheung mentioned.
Has there been any work on this feature? I'm trying to create my own component that uses the same format @brennancheung laid out but I'm banging my head against the wall trying to get it to work.
Ok. I managed to get something working. It's probably horribly inefficient but it works. I'm using graphql() behind the scenes:
import React from 'react';
import {graphql, compose} from 'react-apollo';
export default function Graphql(mutationProps) {
const {mutations, queries, render} = mutationProps;
const composedMutationFuncs = compose(...Object.keys(mutations).map(v => {
const {mutation, options} = mutations[v];
return graphql(mutation, {
name: v,
alias: v,
options,
});
}));
const composedQueryFuncs = compose(...Object.keys(queries).map(v => {
const {query, options} = queries[v];
return graphql(query, {
name: v,
alias: v,
options,
});
}));
const SubCompInstance = compose(composedQueryFuncs, composedMutationFuncs)(subCompProps => {
const mutates = subCompProps.mutationNames.reduce((memo, mutationName) => ({
...memo,
[mutationName]: subCompProps[mutationName],
}), {});
const data = subCompProps.queryNames.reduce((memo, queryName) => ({
...memo,
[queryName]: subCompProps[queryName],
}), {});
const loading = subCompProps.queryNames.reduce((memo, queryName) => memo || subCompProps[queryName].loading, false);
return subCompProps.render(mutates, data, {loading});
});
return <SubCompInstance render={render} mutationNames={Object.keys(mutations)} queryNames={Object.keys(queries)}/>;
}
I use it like this:
function MyComp(props) {
const mutations = {
myFirstMutation: {mutation: gql`.....`},
mySecondMutation: {mutation: gql`.....`, options: {}},
};
const queries = {
myFirstQuery: {
query: gql`...`,
options: {
variables: {someVar: 2},
},
}
return (
<Graphql mutations={mutations} queries={queries} render={(mutates, data, {loading}) => {
// loading is true if any of the queries are currently loading.
if (loading) return <div>Loading...</div>;
// I can call mutates.myFirstMutation({variables: foo});
return <div>{data.myFirstQuery.myquery.bar}</div>;
}}/>
);
}
It mimics the render prop style of render here, you can rename it to children if you want).
All the suggestions above ignore many parts of the new render prop components, e.g. onError, onCompleted, update, handling loading, and errors etc so doesnt make much sense. Its easier to look for a solution around the react render prop pattern than from apollo, e.g. https://github.com/jamesplease/react-composer . Maybe in the future apollo will publish similar to compose for hocs, e.g. Compose component for that.
If you mean conditionally changing the gql query based on a prop, I just used a variable to hold the query, and conditionally changed it based on the prop.
...
import { ADD_BUILD, EDIT_BUILD } from '../../../data/gql_queries/builds'
...
render() {
const { is_editing } = this.props
let mutation_query
if (is_editing) {
mutation_query = EDIT_BUILD
build['id'] = current_build.id // add id to the build object (from db), if editing
}
else {
mutation_query = ADD_BUILD
}
...
return (
<Mutation
mutation={ mutation_query }
variables={ {input: build} }
>
{(query_name, {data}) => {
)
}
Any news on this ?
brennancheung's idea looks perfect to my eyes...
I have a component which can do 2 different actions, create and delete and I really don't want / can't to split them into different components as in my case I hand 2 those mutations into 2 callback props to a library. <MyLibrary createAction={callBackToMutation} deleteAction={callbackToMutation} />
The idea above would just need a little enhancement to handle other props as update. (such as <Mutation mutations={ addItem: {mutation: ADD_ITEM, update: () => ..}, deleteItem: {mutation: DELETE_ITEM, update: () => ...} }>)
Or it could even be a new component Mutations wich take as props named mutations:
<Mutations
addItem={mutation: ADD_ITEM, update: () => ...}
deleteItem={mutation: DELETE_ITEM, update: () => ...}
>
{({ addItem, deleteItem }) => { ... }
</Mutations>
Anyhow that'd be great to have any working solution beside nesting Mutations ...
Just found react-adopt, which does almost this exact trick for me.
Edit: They have even adressed that very issue: https://github.com/pedronauck/react-adopt#leading-with-multiple-params
Any update on this? Something similar to @brennancheung solution would be ideal
Kind of hoping Hooks makes this a no-op as the false hierarchy would disappear.
I'm also using react-adopt to solve this issue. Take a look at this example with Apollo: https://codesandbox.io/s/3x7n8wyp15
Hi everyone! I believe that the new Apollo hooks, useQuery, useMutation, and useSubscription, adequately address this use case. To demonstrate this, I have converted @Cridda's example that uses react-adopt and modified it to use @apollo/react-hooks here: https://codesandbox.io/s/apollo-and-react-hooks-4vril
This example is by no means perfect, but it serves as a demonstration of how hooks can massively simplify some use cases.
If anyone still wants to pursue a new feature request from this library, I recommend that you make a post here: https://github.com/apollographql/apollo-feature-requests/issues
Thanks!
Here's a small helper if you don't/can't use @apollo/react-hooks
import React from 'react';
import { Mutation } from 'react-apollo';
import Composer from 'react-composer';
import PropTypes from 'prop-types';
const MultipleMutations = ({ mutations, children }) => (
<Composer
components={mutations.map(mutation => ({ render }) => (
<Mutation {...mutation}>
{(mutationFn, mutationState) => render({
execute: mutationFn,
...mutationState,
})}
</Mutation>
))}
>
{children}
</Composer>
);
MultipleMutations.propTypes = {
mutations: PropTypes.arrayOf(PropTypes.object).isRequired,
children: PropTypes.func.isRequired,
};
export default MultipleMutations;
Usage
<MultipleMutations
mutations={[
{ mutation: MUTATION_1, variables: { foo: 'bar' } },
{ mutation: MUTATION_2 },
]}
>
{([ mutation1, mutation2 ]) => (
<>
<button disabled={mutation1.loading} onClick={() => mutation1.execute({ ... })} />
<button disabled={mutation2.loading} onClick={() => mutation2.execute({ ... })} />
</>
)}
</MultipleMutations>
Thank you @bitriddler
Thanks @bitriddler This is awesome!! ... For anyone else looking I had a requirement to refetchQueries
const MultipleMutations = ({ mutations, children }) => (
<Composer
components={mutations.map((mutation) => ({ render }) => (
<Mutation {...mutation}>
{(mutationFn, mutationState) =>
render({
execute: mutationFn,
...mutationState,
})
}
</Mutation>
))}
>
{children}
</Composer>
);
Usage
<MultipleMutations
mutations={[
{ mutation: MUTATION_1, variables: { foo: 'bar' }, refetchQueries: [{ query: SOME_QUERY }] }
{ mutation: MUTATION_2 },
]}
>
{([ mutation1, mutation2 ]) => (
<>
<button disabled={mutation1.loading} onClick={() => mutation1.execute({ ... })} />
<button disabled={mutation2.loading} onClick={() => mutation2.execute({ ... })} />
</>
)}
</MultipleMutations>
How to achieve the same composing result in the case of Apollo hooks?
Thanks.
Most helpful comment
I'm also interested in something very close to this. I have a table with items and the user can perform CRUD operations on any of the items. I was thinking it might be nice to pass in an object instead of just a single mutation query.
Something like: