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;