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
component 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 };
components/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 '../components/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 = compose(
withApollo,
connect(mapStateToProps, mapDispatchToProps),
)(Calculator);
export default WrappedCalculator;
One thing that I've done to pass around an ApolloClient instance is to wrap the ApolloClient in a Provider like so:
ApolloClientProvider.js
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.
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
app.ts
clientService.ts
This code uses apollo-client (v 1.1.1)