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函数的结果对象上的startPolling和stopPolling函数来实现动态轮询。示例
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的数字,包含如下:
loading:以前从未运行过查询,请求现在处于暂挂状态。即使从缓存返回结果,查询仍将具有此网络状态,但无论如何都会调度查询。setVariables:如果查询的变量发生更改并且网络请求被触发,则网络状态将setVariables一直持续到该查询的结果返回。当options.variables他们的查询发生变化时,React用户会看到这一点。fetchMore:表示fetchMore已在此查询上调用,并且创建的网络请求当前正在进行中。refetch:这意味着refetch调用了一个查询,并且重新获取请求当前正在进行中。- unused。
poll:表示轮询查询当前正在进行中。因此,例如,如果您每10秒轮询一次查询,则poll每当发送轮询请求但未解决时,网络状态将切换为每10秒。ready:此查询没有请求正在进行中,并且没有发生错误。一切都好。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。只是query和children是必需的。
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- 与运行时错误
graphQLErrors和networkError性能 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();
浙公网安备 33010602011771号