javascript - Best way to access Apollo GraphQL client inside redux action creators? - Stack Overflow

In the (untested) example code below, if I want to access an instance of the Apollo GraphQL client insi

In the (untested) example code below, if I want to access an instance of the Apollo GraphQL client inside actions/math.js, I have to pass it from the Calculator ponent to event handlers, and from the WrappedCalculator event handlers to the action creators.

This results in a lot of code bloat.

What would be a better way for actions/math.js action creators to access the GraphQL client instance?

Example code:

constants/Queries.js:

const MUTATION_APPEND_TO_AUDIT_TRAIL = gql`
    mutation MutationAppendToAuditTrail($mathOperation: String!, $operand1: Float!, $operand2: Float!) {
        appendToAuditTrail(operation: $mathOperation, operand1: $operand1, operand2: $operand2) {
            id
            operation
            operand1
            operand2
        }
    }
`;

actions/math.js:

import { INCREMENT_TOTAL_BY, MULTIPLY_TOTAL_BY } from '../constants/ActionTypes';
import { getTotal } from '../reducers';

incrementResultBy = (operand, graphQlClient) => (dispatch, getState) {
    // Use selector to get the total prior to the operation.
    const total = getTotal(getState());

    // Send action to add a number to the total in the redux store.
    dispatch({
        type: types.INCREMENT_TOTAL_BY,
        operand,
    });

    // Persist the latest user activity to the server.
    graphQlClient.mutate({
        mutation: MUTATION_APPEND_TO_AUDIT_TRAIL,
        variables: {
            mathOperation: 'ADDITION',
            operand1: total,
            operand2: operand,
          },
        });
};

multiplyResultBy = (operand, graphQlClient) => (dispatch, getState) {
    // Use selector to get the total prior to the operation.
    const total = getTotal(getState());

    // Send action to multiply the total in the redux store by a number.
    dispatch({
        type: types.MULTIPLY_TOTAL_BY,
        operand,
    });

    // Persist the latest user activity to the server.
    graphQlClient.mutate({
        mutation: MUTATION_APPEND_TO_AUDIT_TRAIL,
        variables: {
            mathOperation: 'MULTIPLICATION',
            operand1: total,
            operand2: operand,
          },
        });
};

export { incrementResultBy, multiplyResultBy };

ponents/Calculator.jsx

import React from 'react';
import ApolloClient from 'apollo-client';

const Calculator = ({
  total,
  operand,
  onPlusButtonClick,
  onMultiplyButtonClick,
}) => (
  <div>
    <h2>Perform operation for {total} and {operand}</h2>
    <button id="ADD" onClick={onPlusButtonClick(() => this.props.operand, this.props.client)}>ADD</button><br />
    <button id="MULTIPLY" onClick={() => onMultiplyButtonClick(this.props.operand, this.props.client)}>MULTIPLY</button><br />
  </div>
);

DisplayPanel.propTypes = {
  // Apollo GraphQL client instance.
  client: React.PropTypes.instanceOf(ApolloClient),

  // Props from Redux.
  total: React.PropTypes.number,
  operand: React.PropTypes.number,
  onPlusButtonClick: React.PropTypes.func,
  onMultiplyButtonClick: React.PropTypes.func,
};
export default Calculator;

containers/WrappedCalculator.js

import { connect } from 'react-redux';

import Calculator from '../ponents/Calculator';

import { incrementResultBy, multiplyResultBy } from '../actions';
import { getTotal, getOperand } from '../reducers';

const mapStateToProps = state => ({
  total: getTotal(state),
  operand: getOperand(state),
});

const mapDispatchToProps = dispatch => ({
  onPlusButtonClick: (operand, graphQlClient) => dispatch(incrementResultBy(operand, graphQlClient)),
  onMultiplyButtonClick: (operand, graphQlClient) => dispatch(multiplyResultBy(operand, graphQlClient)),
});

// Generate Apollo-aware, redux-aware higher-order container.
const WrappedCalculator = pose(
  withApollo,
  connect(mapStateToProps, mapDispatchToProps),
)(Calculator);

export default WrappedCalculator;

In the (untested) example code below, if I want to access an instance of the Apollo GraphQL client inside actions/math.js, I have to pass it from the Calculator ponent to event handlers, and from the WrappedCalculator event handlers to the action creators.

This results in a lot of code bloat.

What would be a better way for actions/math.js action creators to access the GraphQL client instance?

Example code:

constants/Queries.js:

const MUTATION_APPEND_TO_AUDIT_TRAIL = gql`
    mutation MutationAppendToAuditTrail($mathOperation: String!, $operand1: Float!, $operand2: Float!) {
        appendToAuditTrail(operation: $mathOperation, operand1: $operand1, operand2: $operand2) {
            id
            operation
            operand1
            operand2
        }
    }
`;

actions/math.js:

import { INCREMENT_TOTAL_BY, MULTIPLY_TOTAL_BY } from '../constants/ActionTypes';
import { getTotal } from '../reducers';

incrementResultBy = (operand, graphQlClient) => (dispatch, getState) {
    // Use selector to get the total prior to the operation.
    const total = getTotal(getState());

    // Send action to add a number to the total in the redux store.
    dispatch({
        type: types.INCREMENT_TOTAL_BY,
        operand,
    });

    // Persist the latest user activity to the server.
    graphQlClient.mutate({
        mutation: MUTATION_APPEND_TO_AUDIT_TRAIL,
        variables: {
            mathOperation: 'ADDITION',
            operand1: total,
            operand2: operand,
          },
        });
};

multiplyResultBy = (operand, graphQlClient) => (dispatch, getState) {
    // Use selector to get the total prior to the operation.
    const total = getTotal(getState());

    // Send action to multiply the total in the redux store by a number.
    dispatch({
        type: types.MULTIPLY_TOTAL_BY,
        operand,
    });

    // Persist the latest user activity to the server.
    graphQlClient.mutate({
        mutation: MUTATION_APPEND_TO_AUDIT_TRAIL,
        variables: {
            mathOperation: 'MULTIPLICATION',
            operand1: total,
            operand2: operand,
          },
        });
};

export { incrementResultBy, multiplyResultBy };

ponents/Calculator.jsx

import React from 'react';
import ApolloClient from 'apollo-client';

const Calculator = ({
  total,
  operand,
  onPlusButtonClick,
  onMultiplyButtonClick,
}) => (
  <div>
    <h2>Perform operation for {total} and {operand}</h2>
    <button id="ADD" onClick={onPlusButtonClick(() => this.props.operand, this.props.client)}>ADD</button><br />
    <button id="MULTIPLY" onClick={() => onMultiplyButtonClick(this.props.operand, this.props.client)}>MULTIPLY</button><br />
  </div>
);

DisplayPanel.propTypes = {
  // Apollo GraphQL client instance.
  client: React.PropTypes.instanceOf(ApolloClient),

  // Props from Redux.
  total: React.PropTypes.number,
  operand: React.PropTypes.number,
  onPlusButtonClick: React.PropTypes.func,
  onMultiplyButtonClick: React.PropTypes.func,
};
export default Calculator;

containers/WrappedCalculator.js

import { connect } from 'react-redux';

import Calculator from '../ponents/Calculator';

import { incrementResultBy, multiplyResultBy } from '../actions';
import { getTotal, getOperand } from '../reducers';

const mapStateToProps = state => ({
  total: getTotal(state),
  operand: getOperand(state),
});

const mapDispatchToProps = dispatch => ({
  onPlusButtonClick: (operand, graphQlClient) => dispatch(incrementResultBy(operand, graphQlClient)),
  onMultiplyButtonClick: (operand, graphQlClient) => dispatch(multiplyResultBy(operand, graphQlClient)),
});

// Generate Apollo-aware, redux-aware higher-order container.
const WrappedCalculator = pose(
  withApollo,
  connect(mapStateToProps, mapDispatchToProps),
)(Calculator);

export default WrappedCalculator;
Share Improve this question edited Feb 27, 2017 at 21:07 Elias E. del Real asked Feb 27, 2017 at 20:55 Elias E. del RealElias E. del Real 691 silver badge5 bronze badges 1
  • 1 Possibly a little late... but since queries and mutations are async, you'd need to use the thunk middleware. Thunk middleware conveniently allows you to supply an extra arg to action creators - so you could configure it to supply the client as an extra arg. See: github./gaearon/redux-thunk#injecting-a-custom-argument – WickyNilliams Commented Aug 22, 2017 at 12:38
Add a ment  | 

3 Answers 3

Reset to default 5

One thing that I've done to pass around an ApolloClient instance is to wrap the ApolloClient in a Provider like so:

ApolloClientProvider.js

class ApolloClientProvider {

  constructor() {
    this.client = new ApolloClient({
      networkInterface: '/graphql'
    })
  }
}

export default new ApolloClientProvider()

This will create a singleton like instance of the ApolloClient and wherever you reference it from will return the same ApolloClient that was initialized when the ApolloClientProvider was first referenced.

import ApolloClientProvider from 'ApolloClientProvider'
const client = ApolloClientProvider.client

In your index.js file, you have const client = new ApolloClient({...}):

instead make it export const client = new ApolloClient({...})

And import it as import { client } from './index', or you can pass through all the props.

Based on wmcbain answer I also created and registered a client provider:

ApolloClientProvider.ts

import ApolloClient, { createNetworkInterface } from "apollo-client";
export class ApolloClientProvider {

client: ApolloClient;

constructor(private settings: Settings) {
  this.client = new ApolloClient({
     networkInterface: createNetworkInterface({
       uri: "https://myGraphQLServer/api/data"
     })
  })
 }
}

app.ts

//register the provider
app.service("apolloClientProvider", ApolloClientProvider);

clientService.ts

class ClientService {

apolloClient: ApolloClient;

constructor(private apolloClientProvider: ApolloClientProvider) {
    this.apolloClient = apolloClientProvider.client;
}

This code uses apollo-client (v 1.1.1)

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744797683a4594303.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信