当新订阅到达react-apollo时如何重新查询

时间:2018-07-23 22:16:54

标签: react-apollo

我想知道当订阅接收到新数据时,是否有一种优雅的方法可以在react-apollo中触发查询的重新获取(此处的数据并不重要,将与先前的数据相同)。我只是在这里使用预订作为通知触发器,告诉Query进行重新提取。

我尝试使用Subscription组件和subscribeToMore在Query的子组件中调用“ refetch”方法,但是这两种方法都会导致无限次重新获取。

注意:我正在使用react-apollo v2.1.3和apollo-client v2.3.5

这是简化的代码

<Query
  query={GET_QUERY}
  variables={{ blah: 'test' }}
>
  {({ data, refetch }) => (
    <CustomComponent data={data} />
    //put subscription here? It'll cause infinite re-rendering/refetch loop
  )}
<Query>

2 个答案:

答案 0 :(得分:2)

如果您在Subscription渲染道具功能渲染的组件中使用componentDidMountcomponentDidUpdate,则可能。

该示例使用recompose个高阶组件来避免过多的重复电镀。看起来像这样:

 /*
 * Component rendered when there's data from subscription
 */
export const SubscriptionHandler = compose(
  // This would be the query you want to refetch
  graphql(QUERY_GQL, { 
    name: 'queryName'
  }),
  lifecycle({

    refetchQuery() {
      // condition to refetch based on subscription data received
      if (this.props.data) {  
        this.props.queryName.refetch()
      }
    },

    componentDidMount() {
      this.refetchQuery();
    },

    componentDidUpdate() {
      this.refetchQuery();
    }
  })
)(UIComponent);


/*
 * Component that creates the subscription operation
 */
const Subscriber = ({ username }) => {
  return (
    <Subscription
      subscription={SUBSCRIPTION_GQL}
      variables={{ ...variables }}
    >
      {({ data, loading, error }) => {
        if (loading || error) {
          return null;
        }
        return <SubscriptionHandler data={data} />;
      }}
    </Subscription>
  );
});

另一种在完全分离查询和订阅组件的同时避免重复渲染的循环的方法是使用Apollo Automatic Cache updates

                 +------------------------------------------+
                 |                                          |
    +----------->|  Apollo Store                            |
    |            |                                          |
    |            +------------------------------+-----------+
    +                                           |
client.query                                    |
    ^            +-----------------+  +---------v-----------+
    |            |                 |  |                     |
    |            | Subscription    |  | Query               |
    |            |                 |  |                     |
    |            |                 |  | +-----------------+ |
    |            |  renderNothing  |  | |                 | |
    +------------+                 |  | | Component       | |
                 |                 |  | |                 | |
                 |                 |  | +-----------------+ |
                 |                 |  |                     |
                 +-----------------+  +---------------------+
const Component =() => (
  <div>
    <Subscriber />
    <QueryComponent />
  </div>
)

/*
 * Component that only renders Query data 
 * updated automatically on query cache updates thanks to 
 * apollo automatic cache updates
 */
const QueryComponent = graphql(QUERY_GQL, { 
  name: 'queryName'
})(() => {  
  return (
    <JSX />
  );
});

/*
 * Component that creates the subscription operation
 */
const Subscriber = ({ username }) => {
  return (
    <Subscription
      subscription={SUBSCRIPTION_GQL}
      variables={{ ...variables }}
    >
      {({ data, loading, error }) => {
        if (loading || error) {
          return null;
        }
        return <SubscriptionHandler data={data} />;
      }}
    </Subscription>
  );
});

/*
* Component rendered when there's data from subscription
*/
const SubscriptionHandler = compose(

  // This would be the query you want to refetch
  lifecycle({

    refetchQuery() {
      // condition to refetch based on subscription data received
      if (this.props.data) {  
        var variables = {
            ...this.props.data // if you need subscription data for the variables
        };

        // Fetch the query, will automatically update the cache
        // and cause QueryComponent re-render
        this.client.query(QUERY_GQL, {
          variables: {
            ...variables
          }
        });
      }
    },

    componentDidMount() {
      this.refetchQuery();
    },

    componentDidUpdate() {
      this.refetchQuery();
    }
  }),        
  renderNothing
)();


/*
* Component that creates the subscription operation
*/
const Subscriber = ({ username }) => {
    return (
        <Subscription
        subscription={SUBSCRIPTION_GQL}
        variables={{ ...variables }}
        >
        {({ data, loading, error }) => {
            if (loading || error) {
            return null;
            }
            return <SubscriptionHandler data={data} />;
        }}
        </Subscription>
    );
});

注意: composelifecyclerecompose的方法,可以更轻松地进行更清晰的高阶合成。

答案 1 :(得分:2)

最后,我从佩德罗的答案中得到了灵感。

思路:我面临的问题是我想在Subscription中调用Query的 refetch 方法,但是,Query和Subscription组件只能在render中访问方法。这是无限的刷新/重新渲染的根本原因。为了解决该问题,我们需要将订阅逻辑移出render方法,并将其放置在生命周期方法(即 componentDidMount )中的某个位置,在该方法中,重新触发触发后将不会再次调用它。然后,我决定使用 graphql hoc代替Query组件,以便可以在组件的顶层注入诸如 refetch subscribeToMore 之类的道具,这使它们可以通过任何生命周期方法进行访问。

代码示例(简化版):

class CustomComponent extends React.Component {
  componentDidMount() {
    const { data: { refetch, subscribeToMore }} = this.props;

    this.unsubscribe = subscribeToMore({
      document: <SUBSCRIBE_GRAPHQL>,
      variables: { test: 'blah' },
      updateQuery: (prev) => {
        refetch();
        return prev;
      },     
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    const { data: queryResults, loading, error } } = this.props;

    if (loading || error) return null;

    return <WhatEverYouWant with={queryResults} />
  }
}

export default graphql(GET_QUERY)(CustomComponent);