Search code examples
reactjsreduxgraphqlcreate-react-app

How to use GraphQL Mutations inside a Redux Reducer


I have here a react stateless function that I would love to call my mutations from my reducer. Note, calling the mutation inside a React.Component Class works by means of adding a function inside the component, I dont want that, I want to use my reducer to do that.

With minimum code example, I'll show my setup

// index.js render function


[...] // imported variables

<ApolloProvider client={client}>
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <div>
        <App />
      </div>
    </ConnectedRouter>
  </Provider>
</ApolloProvider>

// Mutation
const Foo = gql`
  mutation Foo($id: String) {
    AddBar(id: $id) {
     id
    }
  }
`;

// Component
[...] // other imports for below variables
const Comp = (props) => (
  <div>
    <button onClick={() => props.addFoo(props)}>Click</button>
  </div>
)

const mapDispatchToProps = dispatch => bindActionCreators({
  addFoo
}, dispatch)

export default connect(
 mapDispatchToProps
)(graphql(Foo)(Comp))

// Reducer (very slimmed with just the action)

export const addFoo = (props) => {

 // props contained the mutate function 
 props.mutate({

   variables: {id: "1" }

 })

  // dispatch goes here but not needed.

}

Ok, I have slimmed down this example as much as possible. My issue is that my variables are not passing to my mutation function. If I hard-code id with one and click the button, graphql changes my data (yes) but the only issue is variables are not passing. In the inspector I do see the variables with correct values but...not passing to the mutate function.


Solution

  • A few thoughts...

    First, reducers should never mutate data or make asynchronous calls. They are meant to be pure functions without side effects.

    Taking GraphQL out of the picture for a moment and assuming that you have just a REST call, you'd usually mutate inside of an Action Creator or something similar. Action Creators in Redux are inherently synchronous, so you'd employ either Redux Thunks or Redux Sagas (or something similar) to help with that.

    Ok, let's put GraphQL back in. As you pointed out, if you include your mutation in your component, it is difficult to wire that in to your Redux implementation. The two are kind of mutually exclusive. In your Redux implementation, however you'd normally make async fetch calls, you can use the Apollo Client without React to mutate...

    const apolloClient = createApolloClient();
    apolloClient.mutate({mutation: Foo}).then(handleResult)
    

    Now, what does createApolloClient() do? You don't want to create a new one every time. That client maintains a cache and can handle all of the value-add which comes from re-using the client. That goes for the React code. You'd like for any queries to be executed from the React bindings to use the same client which you use in your Redux action creators. That createApolloClient() function needs to create a client singleton and return it so that you'd use it in your ApolloProvider as well:

    <ApolloProvider client={createApolloClient()}>
    

    I hope this helps!