React-apollo v2 - Youshido GraphQlBundle - 同时重新获取两个查询

React-apollo v2 - Youshido GraphQlBundle - refetch two queries simultaneously

我正在尝试实施 apollo 来满足我客户的 table 组件。

import CustomersTable from 'components/Customer/CustomersTable';

这个 table 必须是可过滤的,sortable 和分页的。我在 MySQL table 中有 200.000 位客户。这就是为什么过滤器、排序和分页是在服务器端计算的。我需要查询 单独 分页的客户总数,以及客户列表。

import GET_CUSTOMERS_PAGINATED_QUERY from './getCustomersPaginated.graphql';
import GET_CUSTOMERS_PAGINATED_COUNT_QUERY from './getCustomersPaginatedCount.graphql';

出乎意料的是,当 filtersInput 发生变化时,refetch 函数被调用 两次 。第一次使用正确的新变量,第二次使用 初始变量 。所以客户总数被覆盖

const initialFilters = {
  filterId: null,
  filterSiren: null,
  filterName: null,
  filterEmail: null,
};

const getCustomersPaginatedCountOptions = {
  name: 'customersPaginatedCount',
  options() {
    return {
      variables: {
        ...initialFilters,
      },
      fetchPolicy: 'network-only',
    };
  },
  props({ customersPaginatedCount }) {
    return {
      customersPaginatedCount: customersPaginatedCount,
    };
  },
};
const getCustomersPaginatedOptions = {
  name: 'customersPaginated',
  options({ offset, limit }) {
    return {
      variables: {
        offset: offset,
        limit: limit,
        ...initialFilters,
      },
      fetchPolicy: 'network-only',
    };
  },
  props({ customersPaginated }) {
    return {
      customersPaginated: customersPaginated,
    };
  },
};

这两个查询由建议组成(没有错误):

@compose(
  graphql(GET_CUSTOMERS_PAGINATED_QUERY, getCustomersPaginatedOptions),
  graphql(GET_CUSTOMERS_PAGINATED_COUNT_QUERY, getCustomersPaginatedCountOptions),
)
export default class CustomersTableContainer extends React.PureComponent {

  state = {
    offset: this.props.offset,
    limit: this.props.limit,
    pageSize: 10,
    currentPage: 0,
    filters: initialFilters,
    currentOnFilterChangeTimeoutID: null,
  };

  constructor(props) {
    super(props);

    this.onCurrentPageChange = this.onCurrentPageChange.bind(this);
    this.onFiltersChange = this.onFiltersChange.bind(this);
  }

  onCurrentPageChange(newPage) {
    const { customersPaginated } = this.props;
    const { limit, filters } = this.state;

    customersPaginated.refetch({
      offset: newPage * limit,
      ...filters,
    });

    this.setState({ currentPage: newPage });
  }

  onFiltersChange(args) {
    const { customersPaginated, customersPaginatedCount } = this.props;
    const { limit } = this.state;

    const newFilters = Object.assign({}, initialFilters);
    for ( const i in args ) {
      newFilters['filter' + ucfirst(args[i].columnName)] = args[i].value;
    }

    customersPaginated.refetch({
      offset: 0 * limit,
      ...newFilters,
    });

    // --- >> THE REFETCH FUNCTION IS TRIGGERED TWICE HERE ! << ---
    customersPaginatedCount.refetch({
      ...newFilters,
    });

    // here 'test' is displayed once, so onFiltersChange is called once too as expected
    console.log('test');


    this.setState({
      currentPage: 0,
      filters: newFilters,
    });
  }

  render () {
    const { customersPaginated, customersPaginatedCount } = this.props;
    const { currentPage, pageSize } = this.state;

    if (customersPaginated.error) console.error( customersPaginated.error );
    if (customersPaginatedCount.error) console.error( customersPaginatedCount.error );


    return (
      <div>
        {(customersPaginated.error || customersPaginatedCount.error) && (
          <Typography color="error" gutterBottom>
            Une erreur est survenue.
          </Typography>
        )}
        <div>
          <CustomersTable
            customers={customersPaginated.customersPaginated}
            currentPage={currentPage}
            onCurrentPageChange={this.onCurrentPageChange}
            onFiltersChange={this.onFiltersChange}
            pageSize={pageSize}
            totalCount={customersPaginatedCount.customersPaginatedCount || 0}
          />
          {(customersPaginated.loading || customersPaginatedCount.loading) && <Loading />}
        </div>
      </div>
    );
  }

  static propTypes = {
    customersPaginated: PropTypes.object.isRequired,
    customersPaginatedCount: PropTypes.object.isRequired,
    offset: PropTypes.number.isRequired,
    limit: PropTypes.number.isRequired,
  };
}

我的控制台以预期的行为登录组件加载:

{variables: {filterId: null, filterSiren: null, filterName: null, filterEmail: null}, operationName: "getCustomersPaginatedCount"
{variables: {filterId: null, filterSiren: null, filterName: null, filterEmail: null}, operationName: "getCustomersPaginated"

我的控制台以 意外 行为记录过滤器输入更改:

{variables: {filterId: null, filterSiren: null, filterName: "example of customer name", filterEmail: null}, operationName: "getCustomersPaginated"
{variables: {filterId: null, filterSiren: null, filterName: "example of customer name", filterEmail: null}, operationName: "getCustomersPaginatedCount"
{variables: {filterId: null, filterSiren: null, filterName: null, filterEmail: null}, operationName: "getCustomersPaginatedCount"

getCustomersPaginated.graphql :

query getCustomersPaginated(
    $filterId: Int,
    $filterSiren: String,
    $filterName: String,
    $filterEmail: String,
    $offset: Int,
    $limit: Int
  ) {
    customersPaginated(
      filterId: $filterId,
      filterSiren: $filterSiren,
      filterName: $filterName,
      filterEmail: $filterEmail,
      offset: $offset,
      limit: $limit
    ) {
    id
    name
    siren
    email
    activity {
      id
      name
      shortName
      code
    }
    salesFollower {
      id
      username
      firstname
      lastname
      email
      initials
      enabled
    }
    customerGroup {
      id
      name
      code
      enabled
    }
    coreBusiness {
      id
      name
      codeApe
      codeNaf
    }
  }
}

getCustomersPaginatedCount.graphql :

query getCustomersPaginatedCount(
  $filterId: Int,
  $filterSiren: String,
  $filterName: String,
  $filterEmail: String
) {
  customersPaginatedCount(
    filterId: $filterId,
    filterSiren: $filterSiren,
    filterName: $filterName,
    filterEmail: $filterEmail,
  )
}

我的环境:

前面 : reactjs 与 react-apollo

返回 : PHP 7 with Symfony3 and Youshido\GraphQLBundle

我今年开始反应,这个月开始阿波罗。 也许我没有像我应该的那样使用 refetch,也许有更好的方法,或者可能有一个错误(我将 apollo-client-preset 从 1.0.2 更新到 1.0.3,但没有看到任何变化)。 也许 Youshido 有一个解决方案,可以在一个查询中获取客户列表和客户数量。

感谢您的帮助。

在某些情况下,refetch 函数不是必需的。 感谢@stelmakh 在这方面的帮助 issue !!

我的新代码: Child :

import React from 'react';
import PropTypes from 'prop-types';
import { compose, graphql } from 'react-apollo';
import { ucfirst } from 'utils/string';

import CustomersTable from 'components/Customer/CustomersTable';
import Typography from 'material-ui/Typography';

import Loading from 'components/Loading/Loading';

import GET_CUSTOMERS_PAGINATED_QUERY from './getCustomersPaginated.graphql';
import GET_CUSTOMERS_PAGINATED_COUNT_QUERY from './getCustomersPaginatedCount.graphql';


const getCustomersPaginatedCountOptions = {
  name: 'customersPaginatedCount',
  options({ variables }) {
    return {
      variables: variables,
      fetchPolicy: 'network-only',
    };
  },
  props({ customersPaginatedCount }) {
    return { customersPaginatedCount: customersPaginatedCount };
  },
};
const getCustomersPaginatedOptions = {
  name: 'customersPaginated',
  options({ variables }) {
    return {
      variables: variables,
      fetchPolicy: 'network-only',
    };
  },
  props({ customersPaginated }) {
    return { customersPaginated: customersPaginated };
  },
};

@compose(
  graphql(GET_CUSTOMERS_PAGINATED_QUERY, getCustomersPaginatedOptions),
  graphql(GET_CUSTOMERS_PAGINATED_COUNT_QUERY, getCustomersPaginatedCountOptions),
)
export default class CustomersTableContainer extends React.PureComponent {

  state = {
    currentOnFilterChangeTimeoutID: null,
  };

  constructor(props) {
    super(props);

    this.onCurrentPageChange = this.onCurrentPageChange.bind(this);
    this.onSortingChange = this.onSortingChange.bind(this);
    this.onFiltersChange = this.onFiltersChange.bind(this);
  }

  onCurrentPageChange(newPage) {
    const { onChange, variables } = this.props;

    onChange({
      currentPage: newPage,
      'offset': newPage * variables.limit,
    });
  }

  onFiltersChange(args) {
    clearTimeout(this.state.currentOnFilterChangeTimeoutID);

    const newCurrentOnFilterChangeTimeoutID = setTimeout(() => {
      const { onChange, variables } = this.props;

      const newVariables = Object.assign({}, variables);

      if (args.length > 0) {
        for ( const i in args ) {
          newVariables['filter' + ucfirst(args[i].columnName)] = args[i].value;
        }
      } else {
        for ( const i in newVariables ) {
          if (i.substr(0, 6) === 'filter') newVariables[i] = null;
        }
      }

      onChange({
        ...newVariables,
        'currentPage': 0,
        'offset': 0 * variables.limit,
      });
    }, 1000);

    this.setState({ currentOnFilterChangeTimeoutID: newCurrentOnFilterChangeTimeoutID });
  }

  render () {
    const { variables, customersPaginated, customersPaginatedCount } = this.props;

    if (customersPaginated.error) console.error( customersPaginated.error );
    if (customersPaginatedCount.error) console.error( customersPaginatedCount.error );


    return (
      <div>
        {(customersPaginated.error || customersPaginatedCount.error) && (
          <Typography color="error" gutterBottom>
            Une erreur est survenue.
          </Typography>
        )}
        <div>
          <CustomersTable
            customers={customersPaginated.customersPaginated}
            currentPage={variables.currentPage}
            onCurrentPageChange={this.onCurrentPageChange}
            onSortingChange={this.onSortingChange}
            onFiltersChange={this.onFiltersChange}
            pageSize={variables.pageSize}
            totalCount={customersPaginatedCount.customersPaginatedCount || 0}
          />
          {(customersPaginated.loading || customersPaginatedCount.loading) && <Loading />}
        </div>
      </div>
    );
  }

  static propTypes = {
    customersPaginated: PropTypes.object.isRequired,
    customersPaginatedCount: PropTypes.object.isRequired,
    variables: PropTypes.object.isRequired,
    onChange: PropTypes.func.isRequired,
  };
}

Parent :

import React from 'react';

import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import AddIcon from 'material-ui-icons/Add';

import CustomersTableContainer from 'containers/Customer/CustomersTableContainer';

export default class CustomersPage extends React.PureComponent {

  constructor(props) {
    super(props);

    this.state = {
      customersTableVariables: {
        filterId: null,
        filterSiren: null,
        filterName: null,
        filterEmail: null,
        pageSize: 10,
        currentPage: 0,
        offset: 0,
        limit: 10,
      },
    };

    this.onCustomersChange = this.onCustomersChange.bind(this);
  }

  onCustomersChange (newVariables) {
    this.setState({
      customersTableVariables: Object.assign({}, this.state.customersTableVariables, newVariables)
    });
  }

  render () {
    const { customersTableVariables } = this.state;
    return (
      <div>
        <Typography align="right">
          <Button fab color="primary" aria-label="add" href="/customer/new">
            <AddIcon />
          </Button>
        </Typography>
        <Typography type="title" gutterBottom>
          Clients/Prospects
        </Typography>
        <CustomersTableContainer variables={customersTableVariables} onChange={this.onCustomersChange} />
      </div>
    );
  }
}