Apollo-client学习笔记(2)Queries 使用Query组件获取数据

一、Query component

Query component 是Apollo app中最重要的一个组成部分。

1、Query component接受的props

需要提供两个参数(props)给Query,

一个参数提供给query:是一个标准GraphQL document,一般通过使用gql包裹字符串转化得到。

另一个提供给children:接受一个function告诉React 应该渲染什么组件。

Query组件使用了React当中的render prop模式。render prop就是在prop上定义成function,则该function中便可以引用Query组件的state.children是一个特殊的prop,因此可以直接在Query组件内部定义一个function,在function中定义return。render prop模式可以实现组件之间数据的共享。Qurey通过render prop提供了一个对象包含{loading, error, data },供组件使用。

import gql from "graphql-tag";
import { Query } from "react-apollo";

const GET_DOGS = gql`
  {
    dogs {
      id
      breed
    }
  }
`;

const Dogs = ({ onDogSelected }) => (
  <Query query={GET_DOGS}>
    {({ loading, error, data }) => {
      if (loading) return "Loading...";
      if (error) return `Error! ${error.message}`;

      return (
        <select name="dog" onChange={onDogSelected}>
          {data.dogs.map(dog => (
            <option key={dog.id} value={dog.breed}>
              {dog.breed}
            </option>
          ))}
        </select>
      );
    }}
  </Query>
);

  第一步:定义一个GraphQL document 通过gql,本例中为GET_DOGS

  第二步:将GET_DOGS传递给Qurey组件的query prop;

  第三步:传递给Query 组件children一个function,该funciton可以接受到来自Qurey组件的一个对象,包含{loading,error,data}

  第四步:通过给定的{loading,error,data},return一个组件

2、接受数据

流程:

(1)当Query组件挂载后,Apollo Client会为我们的Query创建一个观察者模式,通过订阅Apollo Client cache 查询结果。

(2)首先Query组件试图从缓存中查询结果,如果缓存中没有的话,则向服务器发送请求。

(3)一旦数据从服务器返回,将会被自动存放在缓存中。由于Query组件订阅了缓存,因此它得到了数据。

const GET_DOG_PHOTO = gql`
  query Dog($breed: String!) {
    dog(breed: $breed) {
      id
      displayImage
    }
  }
`;

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

      return (
        <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
      );
    }}
  </Query>
);

  Qurey 组件中的variables是为了向query中传递参数.

  当你第二次选择时,可以看到图片立刻加载了,这是由于缓存中已经有了相关的数据,因此不会再次向服务器发送请求了。

3、更新数据:轮询(polling)和重新获取(refetching)

两种更新数据的方式:polling和refetching

polling:就是每隔一段时间更新一下数据。要实现轮询,只需将pollInterval传递Query组件的prop,间隔为ms。如果传入0,查询将不会轮询。您还可以通过使用传递给render prop函数的结果对象上的startPollingstopPolling函数来实现动态轮询。示例

const DogPhoto = ({ breed }) => (
  <Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
    pollInterval={500}
  >
    {({ loading, error, data, startPolling, stopPolling }) => {
      if (loading) return null;
      if (error) return `Error!: ${error}`;

      return (
        <img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
      );
    }}
  </Query>
);

  如果是希望响应user的动作进行更新数据,则可以使用refetch funciton.

const DogPhoto = ({ breed }) => (
  <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>
);

  

4、loading 和 error state

注意到,如果我们点击refetch按钮,页面并不会重新显示loading,而是在图片到达后直接更新。如果我们希望提示用户正在更新。则可以使用networkStatus来通知用户。首先我们需要在props中设置notifyOnNetworkStatusChange为true,这样就可以得到networkStatus信息。

示例:

const DogPhoto = ({ breed }) => (
  <Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
    notifyOnNetworkStatusChange
  >
    {({ loading, error, data, refetch, networkStatus }) => {
      if (networkStatus === 4) return "Refetching!";
      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>
);

  networkStatus是从1-8的数字,包含如下:

  1. loading:以前从未运行过查询,请求现在处于暂挂状态。即使从缓存返回结果,查询仍将具有此网络状态,但无论如何都会调度查询。
  2. setVariables:如果查询的变量发生更改并且网络请求被触发,则网络状态将setVariables一直持续到该查询的结果返回。options.variables他们的查询发生变化时,React用户会看到这一点
  3. fetchMore:表示fetchMore已在此查询上调用,并且创建的网络请求当前正在进行中。
  4. refetch:这意味着refetch调用了一个查询,并且重新获取请求当前正在进行中。
  5. unused。
  6. poll:表示轮询查询当前正在进行中。因此,例如,如果您每10秒轮询一次查询,则poll每当发送轮询请求但未解决时,网络状态将切换为每10秒。
  7. ready:此查询没有请求正在进行中,并且没有发生错误。一切都好。
  8. error:此查询没有正在进行的请求,但检测到一个或多个错误。

5、手动触发查询

Qurey组件挂载时,Apollo client 会自动触发查询,如果我们不希望自动触发,而是希望用户单击按钮时再触发,那么我们可以使用ApolloConsumer组件直接调用client.query()

import React, { Component } from 'react';
import { ApolloConsumer } from 'react-apollo';

class DelayedQuery extends Component {
  state = { dog: null };

  onDogFetched = dog => this.setState(() => ({ dog }));

  render() {
    return (
      <ApolloConsumer>
        {client => (
          <div>
            {this.state.dog && <img src={this.state.dog.displayImage} />}
            <button
              onClick={async () => {
                const { data } = await client.query({
                  query: GET_DOG_PHOTO,
                  variables: { breed: "bulldog" }
                });
                this.onDogFetched(data.dog);
              }}
            >
              Click me!
            </button>
          </div>
        )}
      </ApolloConsumer>
    );
  }
}

  这种方式是比较麻烦的,建议使用Query组件

6、Qurey组件详解:

Query组件接受以下props。只是querychildren必需的

query:DocumentNode
一个GraphQL查询文档解析为AST graphql-tag必需的
children:(结果:QueryResult)=> React.ReactNode
一个函数,根据查询结果返回要渲染的UI。必需的
variables:{[key:string]:any}
包含查询需要执行的所有变量的对象
pollInterval:数字
指定组件轮询数据的时间间隔(以毫秒为单位)。默认为0(无轮询)。
notifyOnNetworkStatusChange:布尔值
是否更新网络状态或网络错误应重新呈现组件。默认为false。
fetchPolicy:FetchPolicy
您希望组件如何与Apollo缓存交互。默认为“cache-first”。
errorPolicy:ErrorPolicy
您希望组件如何处理网络和GraphQL错误。默认为“none”,这意味着我们将GraphQL错误视为运行时错误。
ssr:布尔值
传入false以在服务器端呈现期间跳过查询。
displayName:字符串
要在React DevTools中显示的组件的名称。默认为“查询”。
skip:布尔值
如果skip为true,则将完全跳过查询。
onCompleted:(数据:TData | {})=>无效
查询成功完成后执行回调。
onError:(错误:ApolloError)=>无效
发生错误时执行的回调。
context:记录<string,any>
查询组件与网络接口(Apollo Link)之间的共享上下文。用于从props设置标题或将信息发送到requestApollo Boost 功能。
 

可以使用的数据

传递给childrenprop 的渲染道具功能Query是使用QueryResult具有以下属性的object(调用的此对象包含查询结果,以及一些用于重新获取,动态轮询和分页的有用函数。

data:TData
包含GraphQL查询结果的对象。默认为空对象。
loading:布尔值
一个布尔值,指示请求是否在飞行中
error:ApolloError
与运行时错误graphQLErrorsnetworkError性能
variables:{[key:string]:any}
包含调用查询的变量的对象
networkStatus:NetworkStatus
1-8之间的数字,对应于网络请求的详细状态。包括有关重新获取和轮询状态的信息。notifyOnNetworkStatusChange道具一起使用
refetch:(变量?:TVariables)=>承诺
一个允许您重新获取查询并可选地传入新变量的函数
fetchMore:({query?:DocumentNode,variables?:TVariables,updateQuery:Function})=> Promise
一种为查询分页的函数
startPolling:(间隔:数字)=>无效
此函数以ms为单位设置间隔,并在每次指定的间隔通过时获取查询。
stopPolling:()=>无效
此函数停止查询轮询。
subscribeToMore:(选项:{document:DocumentNode,variables?:TVariables,updateQuery?:Function,onError?:Function})=>()=> void
设置订阅的功能subscribeToMore返回一个可用于取消订阅的函数。
updateQuery:(previousResult:TData,options:{variables:TVariables})=> TData
一种函数,允许您在query,mutatation或subscribe的上下文之外的缓存中更新查询的结果
client:ApolloClient
你的ApolloClient实例。用于手动触发查询或将数据写入缓存。

 完整的示例:

const client = new ApolloClient({uri:`https://nx9zvp49q7.lp.gql.zone/graphql`})

const GET_DOG_PHOTO = gql`
  query Dog($breed: String!) {
    dog(breed: $breed) {
      id
      displayImage
    }
  }
`;

const DogPhoto = ({ breed }) => (
  <Query
    query={GET_DOG_PHOTO}
    variables={{ breed }}
    skip={!breed}
    notifyOnNetworkStatusChange
    errorPolicy="all"
    // pollInterval={500}
    >
    {({ loading, error, data,refetch,networkStatus }) => {
      if(networkStatus===4) return "refetching!"
      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>
);

const GET_DOGS = gql`
  {
    dogs {
      id
      breed
    }
  }
`;

const Dogs = ({ onDogSelected }) => (
  <Query query={GET_DOGS}>
    {({ loading, error, data }) => {
      if (loading) return "Loading...";
      if (error) return `Error! ${error.message}`;

      return (
        <select name="dog" onChange={onDogSelected}>
          {data.dogs.map(dog => (
            <option key={dog.id} value={dog.breed}>
              <DogPhoto breed={dog.breed} />
              {/* {dog.breed} */}
            </option>
          ))}
        </select>
      );
    }}
  </Query>
);


class App extends React.Component {
  state = { selectedDog: null };

  onDogSelected = ({ target }) => {
    this.setState(() => ({ selectedDog: target.value }));
  };

  render() {
    return (
      <ApolloProvider client={client}>
        <div>
          <Dogs onDogSelected={this.onDogSelected}/>

        </div>
      </ApolloProvider>
    );
  }
}

ReactDOM.render(
    <App />,
   document.getElementById('root'));
registerServiceWorker();

  

posted @ 2018-08-25 10:00  tutu_python  阅读(1693)  评论(0)    收藏  举报