【问题标题】:Best way to access Apollo GraphQL client inside redux action creators?在 redux action creators 中访问 Apollo GraphQL 客户端的最佳方式是什么?
【发布时间】:2017-07-18 15:32:38
【问题描述】:

在下面的(未经测试的)示例代码中,如果我想访问 actions/math.js 内的 Apollo GraphQL 客户端实例,我必须将它从 Calculator 组件传递给事件处理程序,并从 WrappedCalculator动作创建者的事件处理程序。

这会导致大量代码膨胀。

actions/math.js 操作创建者访问 GraphQL 客户端实例的更好方法是什么?

示例代码:

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;

【问题讨论】:

  • 可能有点晚了...但是由于查询和突变是异步的,因此您需要使用 thunk 中间件。 Thunk 中间件允许您方便地为动作创建者提供额外的 arg - 因此您可以将其配置为将客户端作为额外的 arg 提供。见:github.com/gaearon/redux-thunk#injecting-a-custom-argument

标签: javascript react-redux graphql apollo


【解决方案1】:

在你的 index.js 文件中,你有 const client = new ApolloClient({...}):

改为export const client = new ApolloClient({...})

并将其导入为import { client } from './index',或者你可以通过所有的道具。

【讨论】:

    【解决方案2】:

    根据 wmcbain 的回答,我还创建并注册了一个客户端提供商:

    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;
    }
    

    此代码使用 apollo-client (v 1.1.1)

    【讨论】:

      【解决方案3】:

      我为传递 ApolloClient 实例所做的一件事是将 ApolloClient 包装在 Provider 中,如下所示:

      ApolloClientProvider.js

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

      这将创建一个类似于 ApolloClient 的单例实例,无论您从何处引用它,都将返回在首次引用 ApolloClientProvider 时初始化的相同 ApolloClient。

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

      【讨论】:

        猜你喜欢
        • 2018-06-07
        • 2021-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-08
        • 2018-01-20
        • 2018-05-03
        相关资源
        最近更新 更多