当父组件重新呈现时,如何强制Apollo Query组件重新运行查询

时间:2018-07-26 03:18:59

标签: reactjs graphql react-apollo apollo-client

我在组件中使用Apollo Client的<Query>,该组件在生命周期方法中更改状态时会重新呈现。我希望我的<Query>组件重新运行查询,因为我知道数据已更改。 看来查询组件正在使用一个缓存,该缓存需要在重新运行查询之前无效。

我正在使用一种怪异的解决方法,该方法可将来自呈现器道具的refetch回调缓存在父组件中,但是感觉不对。如果有人感兴趣,我会在答案中张贴我的方法。

我的代码看起来像这样。我从查询中删除了loadingerror处理,为了简洁起见,还删除了其他一些细节。

class ParentComponent extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.refetchId !== prevProps.refetchId) {
      const otherData = this.processData() // do something
      this.setState({otherData}) // this forces component to reload
    }
  }

  render() {
    const { otherData } = this.state

    return (
      <Query query={MY_QUERY}>
        {({ data }) => {
          return <ChildComponent gqlData={data} stateData={otherData} />
        }}
      </Query>
    )
  }
}

如何强制<Query>获取新数据以传递给<ChildComponent>

即使ParentComponent在道具或状态改变时重新渲染,Query也不会重新运行。 ChildComponent获得更新的stateData道具,但是拥有陈旧的gqlData道具。据我了解,Apollo的查询缓存需要无效,但我不确定。

请注意,将refetch传递给ChildComponent并不是答案,因为它仅显示GraphQL的信息,并且不知道何时重新提取。我不想引入计时器或使ChildComponent复杂化来解决此问题-它不需要了解这种复杂性或数据获取问题。

5 个答案:

答案 0 :(得分:2)

在我看来,查询组件不一定必须位于此ParentComponent中。

在那种情况下,我将向上移动Query组件,因为在ChildComponent中没有结果的情况下,我仍然可以渲染其他内容。然后,我将可以访问query.refetch方法。

请注意,在示例中,我添加了graphql临时对象,但是您仍然可以在<ParentComponent />周围使用查询组件。

class ParentComponent extends React.Component {
    componentDidUpdate(prevProps) {
        if (this.props.refetchId !== prevProps.refetchId) {
            const otherData = this.processData() // do something

            //   won't need this anymore, since refetch will cause the Parent component to rerender)
            //   this.setState({otherData}) // this forces component to reload 

            this.props.myQuery.refetch(); // >>> Refetch here!
        }
    }

    render() {
        const {
            otherData
        } = this.state;

        return <ChildComponent gqlData={this.props.myQuery} stateData={otherData} />;
    }
}

export graphql(MY_QUERY, {
    name: 'myQuery'
})(ParentComponent);

答案 1 :(得分:1)

您可以重新获取父组件吗?父组件获得更新后,您就可以评估是否触发提取。

我没有使用Query来完成此操作,如下所示:

class ParentComp extends React.Component {

    lifeCycleHook(e) { //here
        // call your query here
        this.props.someQuery()
    }

    render() {
        return (
            <div>
                <Child Comp data={this.props.data.key}> //child would only need to render data
            </div>
        );
    }
}

export default graphql(someQuery)(SongCreate);

因此您可以随时触发获取。在这种情况下,您可以将查询作为prop

对于您的情况,您可以使用export default graphql(addSongQuery)(SongCreate);将查询放入属性。然后在您的生命周期钩子DidUpdate中调用它。

另一种选择是在查询上使用refetch

<Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
  >
    {({ loading, error, data, refetch }) => {
      if (loading) return null;
      if (error) return `Error!: ${error}`;

      return (
        <div>
          <img
            src={data.dog.displayImage}
            style={{ height: 100, width: 100 }}
          />
          <button onClick={() => refetch()}>Refetch!</button>
        </div>
      );
    }}
  </Query>

第二种方法将要求您将某些信息传递给孩子,这并不是真的那么糟糕。

答案 2 :(得分:1)

我遇到了类似的情况,可以通过fetchPolicy属性来解决:

<Query fetchPolicy="no-cache" ...

任务是通过单击列表项从服务器加载一些详细信息。

如果我想添加一个操作来强制重新获取查询(例如修改详细信息),我首先将refetch分配给this.refetch

<Query fetchPolicy="no-cache" query={GET_ACCOUNT_DETAILS} variables=... }}>
  {({ data: { account }, loading, refetch }) => {
  this.refetch = refetch;
...

在我希望进行重新提取的特定操作中,我致电:

this.refetch();

答案 3 :(得分:0)

如所承诺的,这是我的解决方法

class ParentComponent extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.refetchId !== prevProps.refetchId) {
      const otherData = this.processData() // do something
      this.setState({otherData})
      if (this.refetch) {
        this.refetch()
      }
    }
  }

  render() {
    const { otherData } = this.state
    const setRefetch = refetch => {this.refetch = refetch}
    return (
      <Query query={MY_QUERY}>
        {({ data, refetch }) => {
          setRefetch(refetch)
          return <ChildComponent gqlData={data} stateData={otherData} />
        }}
      </Query>
    )
  }
}

答案 4 :(得分:0)

除了上面已经接受的答案外,还有两种方法可以实现这一目标。

如果只需要从graphql服务器获取数据以在每次安装组件时生成列表,则可以使用以下选项:

  1. 使用选项fetchPolicy="no-cache"
export default defineComponent({
  setup() {
    const { result, loading, error } = useMyQuery(
      () => {
        return { date: new Date() }
      },
      {
        pollInterval: 15 * 60 * 1000, // refresh data every 15 min
        fetchPolicy: 'no-cache',
      }
    )
  1. 使用refetch()方法,同时将缓存保留在适当的位置:
export default defineComponent({
  setup() {
    const { result, loading, error}, refetch } = useMyQuery(
      () => {
        return { date: new Date() }
      },
      {
        pollInterval: 15 * 60 * 1000, // refresh data every 15 min
      }
    )

    if (!loading.value) void refetch()