您的位置:首页 > Web前端 > React

react apollo_2020年的React + Apollo教程(真实示例)

2020-08-21 04:54 573 查看 https://blog.csdn.net/cumi6497

react apollo

If you want to build apps with React and GraphQL, Apollo is the library you should use.

如果要使用React和GraphQL构建应用程序,则应使用Apollo库。

I've put together a comprehensive cheatsheet that goes through all of the core concepts in the Apollo library, showing you how to use it with React from front to back.

我整理了一份全面的备忘单,介绍了Apollo库中的所有核心概念,向您展示了如何从头到尾将它与React一起使用。

想要自己的副本? 📄 (Want Your Own Copy? 📄)

You can grab the PDF cheatsheet right here (it takes 5 seconds).

您可以在此处获取PDF速查表(需要5秒钟)。

Here are some quick wins from grabbing the downloadable version:

从下载可下载版本中可以快速获得一些好处:

  • ✓ Quick reference to review however and whenever

    ✓无论何时何地都可以快速查阅
  • ✓ Tons of useful code snippets based off of real-world projects

    ✓大量基于实际项目的有用代码段
  • ✓ Read this guide offline, wherever you like. On the train, at your desk, standing in line — anywhere.

    ✓随时随地离线阅读本指南。 在火车上,在您的办公桌前,在任何地方排队。

喜欢视频课程吗? 📺 (Prefer Video Lessons? 📺)

A great deal of this cheatsheet is based off of the app built in the React + GraphQL 2020 Crash Course.

该备忘单很大程度上基于React + GraphQL 2020速成课程中构建的应用程序。

If you want some more hands-on video lessons, plus see how to build apps with React, GraphQL and Apollo, you can watch the course right here.

如果您需要更多的动手视频课程,以及了解如何使用React,GraphQL和Apollo构建应用程序,可以在此处观看课程。

Note: This cheatsheet does assume familiarity with React and GraphQL. If you need a quick refresher on GraphQL and how to write it, a great resource is the official GraphQL website.

注意:该备忘单假定您已熟悉React和GraphQL。 如果您需要GraphQL的快速入门以及如何编写它,那么官方的GraphQL网站是一个很好的资源。

目录 (Table of Contents)

入门 (Getting Started)

核心Apollo React挂钩 (Core Apollo React Hooks)

基本食谱 (Essential Recipes)

什么是阿波罗,为什么我们需要它? (What is Apollo and why do we need it?)

Apollo is a library that brings together two incredibly useful technologies used to build web and mobile apps: React and GraphQL.

Apollo是一个库,汇集了用于构建Web和移动应用程序的两种极其有用的技术:React和GraphQL。

React was made for creating great user experiences with JavaScript. GraphQL is a very straightforward and declarative new language to more easily and efficiently fetch and change data, whether it is from a database or even from static files.

React旨在通过JavaScript创造出色的用户体验。 GraphQL是一种非常直接和声明性的新语言,可以更轻松有效地获取和更改数据,无论是从数据库还是从静态文件中获取和更改数据。

Apollo is the glue that binds these two tools together. Plus it makes working with React and GraphQL a lot easier by giving us a lot of custom React hooks and features that enable us to both write GraphQL operations and execute them with JavaScript code.

阿波罗是将这两个工具结合在一起的粘合剂。 另外,它为我们提供了许多自定义的React钩子和功能,使我们能够编写GraphQL操作并使用JavaScript代码执行它们,从而使React和GraphQL的使用变得更加容易。

We'll cover these features in-depth throughout the course of this guide.

在本指南的整个过程中,我们将深入介绍这些功能。

Apollo客户端基本设置 (Apollo Client basic setup)

If you are starting a project with a React template like Create React App, you will need to install the following as your base dependencies to get up and running with Apollo Client:

如果您要使用诸如Create React App之类的React模板启动项目,则需要安装以下内容作为基本依赖项,以启动并运行Apollo Client:

// with npm:
npm i @apollo/react-hooks apollo-boost graphql

// with yarn:
yarn add @apollo/react-hooks apollo-boost graphql

@apollo/react-hooks
gives us React hooks that make performing our operations and working with Apollo client better

@apollo/react-hooks
为我们提供了React挂钩,使我们可以更好地执行操作并与Apollo客户端一起使用

apollo-boost
helps us set up the client along with parse our GraphQL operations

apollo-boost
帮助我们设置客户端以及解析GraphQL操作

graphql
also takes care of parsing the GraphQL operations (along with gql)

graphql
还负责解析GraphQL操作(以及gql)

Apollo客户端+订阅设置 (Apollo Client + subscriptions setup)

To use all manner of GraphQL operations (queries, mutations, and subscriptions), we need to install more specific dependencies as compared to just

apollo-boost
:

要使用所有形式的GraphQL操作(查询,变异和订阅),与

apollo-boost
相比,我们需要安装更多特定的依赖
apollo-boost

// with npm:
npm i @apollo/react-hooks apollo-client graphql graphql-tag apollo-cache-inmemory apollo-link-ws

// with yarn:
yarn add @apollo/react-hooks apollo-client graphql graphql-tag apollo-cache-inmemory apollo-link-ws

apollo-client
gives us the client directly, instead of from
apollo-boost

apollo-client
直接给我们的客户,而不是
apollo-boost

graphql-tag
is integrated into
apollo-boost
, but not included in
apollo-client

graphql-tag
已集成到
apollo-boost
,但未包含在
apollo-client

apollo-cache-inmemory
is needed to setup our own cache (which
apollo-boost
, in comparison, does automatically)

apollo-cache-inmemory
来设置我们自己的缓存(相比之下,
apollo-boost
会自动执行)

apollo-link-ws
is needed for communicating over websockets, which subscriptions require

通过网络套接字进行通信需要

apollo-link-ws
,订阅需要

创建一个新的Apollo客户端(基本设置) (Creating a new Apollo Client (basic setup))

The most straightforward setup for creating an Apollo client is by instantiating a new client and providing just the

uri
property, which will be your GraphQL endpoint:

创建Apollo客户端最直接的设置是实例化一个新客户端并仅提供

uri
属性,该属性将成为您的GraphQL端点:

import ApolloClient from "apollo-boost";

const client = new ApolloClient({
uri: "https://your-graphql-endpoint.com/api/graphql",
});

apollo-boost
was developed in order to make doing things like creating an Apollo Client as easy as possible. What it lacks for the time being, however, is support for GraphQL subscriptions over a websocket connection.

开发

apollo-boost
是为了使诸如创建Apollo客户程序之类的工作尽可能轻松。 但是,目前暂时缺乏通过Websocket连接支持GraphQL订阅的功能。

By default, it performs the operations over an http connection (as you can see through our provided uri above).

默认情况下,它通过http连接执行操作(如您在上面提供的uri中所见)。

In short, use

apollo-boost
to create your client if you only need to execute queries and mutations in your app.

简而言之,如果只需要在应用程序中执行查询和变异,请使用

apollo-boost
创建客户端。

It setups an in-memory cache by default, which is helpful for storing our app data locally. We can read from and write to our cache to prevent having to execute our queries after our data is updated. We'll cover how to do that a bit later.

默认情况下,它会设置内存缓存,这有助于在本地存储我们的应用数据。 我们可以读取和写入缓存,以防止在数据更新后不得不执行查询。 稍后我们将介绍如何执行该操作。

创建一个新的Apollo Client(+订阅设置) (Creating a new Apollo Client (+ subscriptions setup))

Subscriptions are useful for more easily displaying the result of data changes (through mutations) in our app.

订阅有助于在我们的应用中更轻松地显示数据更改的结果(通过突变)。

Generally speaking, we use subscriptions as an improved kind of query. Subscriptions use a websocket connection to 'subscribe' to updates and data, enabling new or updated data to be immediately displayed to our users without having to reexecute queries or update the cache.

一般来说,我们使用订阅作为一种改进的查询。 订阅使用websocket连接来“订阅”更新和数据,从而使新数据或更新数据可以立即显示给我们的用户,而无需重新执行查询或更新缓存。

import ApolloClient from "apollo-client";
import { WebSocketLink } from "apollo-link-ws";
import { InMemoryCache } from "apollo-cache-inmemory";

const client = new ApolloClient({
link: new WebSocketLink({
uri: "wss://your-graphql-endpoint.com/v1/graphql",
options: {
reconnect: true,
connectionParams: {
headers: {
Authorization: "Bearer yourauthtoken",
},
},
},
}),
cache: new InMemoryCache(),
});

向客户端提供React组件 (Providing the client to React components)

After creating a new client, passing it to all components is essential in order to be able to use it within our components to perform all of the available GraphQL operations.

创建新客户端后,将其传递给所有组件是必不可少的,以便能够在我们的组件中使用它来执行所有可用的GraphQL操作。

The client is provided to the entire component tree using React Context, but instead of creating our own context, we import a special context provider from

@apollo/react-hooks
called
ApolloProvider
. We can see how it differs from the regular React Context due to it having a special prop,
client
, specifically made to accept the created client.

使用React Context将客户端提供给整个组件树,但是我们没有创建自己的上下文,而是从

@apollo/react-hooks
导入了一个特殊的上下文提供程序
ApolloProvider
。 我们可以看到它是如何不同于常规的背景下做出React,由于其特殊的道具,它
client
,专门用来接受创建的客户端。

Note that all of this setup should be done in your index.js or App.js file (wherever your Routes declared) so that the Provider can be wrapped around all of your components.

请注意,所有这些设置都应在index.js或App.js文件(无论声明了Routes的位置)中完成,以便Provider可以包装在所有组件周围。

import { ApolloProvider } from "@apollo/react-hooks";

const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<BrowserRouter>
<Switch>
<Route exact path="/" component={App} />
<Route exact path="/new" component={NewPost} />
<Route exact path="/edit/:id" component={EditPost} />
</Switch>
</BrowserRouter>
</ApolloProvider>
</React.StrictMode>,
rootElement
);

直接使用客户端 (Using the client directly)

The Apollo client is most important part of the library due to the fact that it is responsible for executing all of the GraphQL operations that we want to perform with React.

Apollo客户端是该库中最重要的部分,因为它负责执行我们要使用React执行的所有GraphQL操作。

We can use the created client directly to perform any operation we like. It has methods corresponding to queries (

client.query()
), mutations (
client.mutate()
), and subscriptions (
client.subscribe()
).

我们可以直接使用创建的客户端执行我们喜欢的任何操作。 它有着与查询(方法

client.query()
突变(
client.mutate()
和订阅(
client.subscribe()

Each method accepts an object and it's own corresponding properties:

每个方法都接受一个对象,并且具有自己的相应属性:

// executing queries
client
.query({
query: GET_POSTS,
variables: { limit: 5 },
})
.then((response) => console.log(response.data))
.catch((err) => console.error(err));

// executing mutations
client
.mutate({
mutation: CREATE_POST,
variables: { title: "Hello", body: "World" },
})
.then((response) => console.log(response.data))
.catch((err) => console.error(err));

// executing subscriptions
client
.subscribe({
subscription: GET_POST,
variables: { id: "8883346c-6dc3-4753-95da-0cc0df750721" },
})
.then((response) => console.log(response.data))
.catch((err) => console.error(err));

Using the client directly can be a bit tricky, however, since in making a request, it returns a promise. To resolve each promise, we either need

.then()
and
.catch()
callbacks as above or to
await
each promise within a function declared with the
async
keyword.

直接使用客户端可能会有些棘手,但是,因为在发出请求时,它会返回承诺。 要解决每个诺言,我们要么如上所述需要

.catch()
.then()
.catch()
回调,要么在用
async
关键字声明的函数中
await
每个诺言。

在.js文件(gql)中编写GraphQL操作 (Writing GraphQL operations in .js files (gql))

Notice above that I didn't specify the contents of the variables

GET_POSTS
,
CREATE_POST
, and
GET_POST
.

注意,上面我没有指定变量

GET_POSTS
CREATE_POST
GET_POST

They are the operations written in the GraphQL syntax which specify how to perform the query, mutation, and subscription respectively. They are what we would write in any GraphiQL console to get and change data.

它们是用GraphQL语法编写的操作,分别指定如何执行查询,更改和订阅。 它们是我们将在任何GraphiQL控制台中编写的用于获取和更改数据的内容。

The issue here, however, is that we can't write and execute GraphQL instructions in JavaScript (.js) files, like our React code has to live in.

但是,这里的问题是,我们无法在JavaScript(.js)文件中编写和执行GraphQL指令,就像我们的React代码必须存在一样。

To parse the GraphQL operations, we use a special function called a tagged template literal to allow us to express them as JavaScript strings. This function is named

gql
.

为了解析GraphQL操作,我们使用一个称为标记模板文字的特殊函数,使我们可以将它们表示为JavaScript字符串。 该函数名为

gql

// if using apollo-boost
import { gql } from "apollo-boost";
// else, you can use a dedicated package graphql-tag
import gql from "graphql-tag";

// query
const GET_POSTS = gql`
query GetPosts($limit: Int) {
posts(limit: $limit) {
id
body
title
createdAt
}
}
`;

// mutation
const CREATE_POST = gql`
mutation CreatePost($title: String!, $body: String!) {
insert_posts(objects: { title: $title, body: $body }) {
affected_rows
}
}
`;

// subscription
const GET_POST = gql`
subscription GetPost($id: uuid!) {
posts(where: { id: { _eq: $id } }) {
id
body
title
createdAt
}
}
`;

useQuery挂钩 (useQuery Hook)

The

useQuery
hook is arguably the most convenient way of performing a GraphQL query, considering that it doesn't return a promise that needs to be resolved.

考虑到它不会返回需要解决的承诺,因此

useQuery
挂钩可以说是执行GraphQL查询的最方便的方法。

It is called at the top of any function component (as all hooks should be) and receives as a first required argument—a query parsed with

gql
.

它在任何函数组件的顶部(如所有钩子一样)被调用,并作为第一个必需的参数接收—用

gql
解析的查询。

It is best used when you have queries that should be executed immediately, when a component is rendered, such as a list of data which the user would want to see immediately when the page loads.

当您具有应立即执行的查询,呈现组件时(例如,用户希望在页面加载后立即看到的数据列表)时,最好使用此方法。

useQuery
returns an object from which we can easily destructure the values that we need. Upon executing a query, there are three primary values will need to use within every component in which we fetch data. They are
loading
,
error
, and
data
.

useQuery
返回一个对象,我们可以从中轻松地解构所需的值。 执行查询后,需要在其中获取数据的每个组件中使用三个主要值。 它们正在
loading
error
data

const GET_POSTS = gql`
query GetPosts($limit: Int) {
posts(limit: $limit) {
id
body
title
createdAt
}
}
`;

function App() {
const { loading, error, data } = useQuery(GET_POSTS, {
variables: { limit: 5 },
});

if (loading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;

return data.posts.map((post) => <Post key={post.id} post={post} />);
}

Before we can display the data that we're fetching, we need to handle when we're loading (when

loading
is set to true) and we are attempting to fetch the data.

在显示正在获取的数据之前,我们需要在加载时进行处理(当

loading
设置为true时),并且我们尝试获取数据。

At that point, we display a div with the text 'Loading' or a loading spinner. We also need to handle the possibility that there is an error in fetching our query, such as if there's a network error or if we made a mistake in writing our query (syntax error).

届时,我们将显示一个带有文本“加载中”的div或一个加载微调器。 我们还需要处理在获取查询时出错的可能性,例如是否存在网络错误或编写查询时出错(语法错误)。

Once we're done loading and there's no error, we can use our data in our component, usually to display to our users (as we are in the example above).

一旦完成加载并且没有错误,我们就可以在组件中使用我们的数据,通常将其显示给我们的用户(如上例所示)。

There are other values which we can destructure from the object that

useQuery
returns, but you'll need
loading
,
error
, and
data
in virtually every component where you execute
useQuery
. You can see a full list of all of the data we can get back from useQuery here.

我们还可以从

useQuery
返回的对象中解构出其他值,但是实际上在执行
useQuery
每个组件中都需要
loading
error
data
。 您可以在此处看到我们可以从useQuery中获取的所有数据的完整列表。

useLazyQuery挂钩 (useLazyQuery Hook)

The

useLazyQuery
hook provides another way to perform a query, which is intended to be executed at some time after the component is rendered or in response to a given data change.

useLazyQuery
挂钩提供了另一种执行查询的方法,该方法旨在在呈现组件之后或响应给定的数据更改后的某个时间执行。

useLazyQuery
is very useful for things that happen at any unknown point of time, such as in response to a user's search operation.

useLazyQuery
对于在任何未知时间点发生的事情(例如响应用户的搜索操作)非常有用。

function Search() {
const [query, setQuery] = React.useState("");
const [searchPosts, { data }] = useLazyQuery(SEARCH_POSTS, {
variables: { query: `%${query}%` },
});
const [results, setResults] = React.useState([]);

React.useEffect(() => {
if (!query) return;
// function for executing query doesn't return a promise
searchPosts();
if (data) {
setResults(data.posts);
}
}, [query, data, searchPosts]);

if (called && loading) return <div>Loading...</div>;

return results.map((result) => (
<SearchResult key={result.id} result={result} />
));
}

useLazyQuery
differs from
useQuery
, first of all, in what's returned from the hook. It returns an array which we can destructure, instead of an object.

useLazyQuery
不同于
useQuery
,首先,在什么从钩回真实。 它返回一个我们可以解构的数组,而不是对象。

Since we want to perform this query sometime after the component is mounted, the first element that we can destructure is a function which you can call to perform that query when you choose. This query function is named

searchPosts
in the example above.

由于我们要在组件安装后的某个时间执行此查询,因此我们可以分解的第一个元素是一个函数,您可以在选择该函数时调用该函数来执行该查询。 在上面的示例中,此查询功能名为

searchPosts

The second destructured value in the array is an object, which we can use object destructuring on and from which we can get all of the same properties as we did from

useQuery
, such as
loading
,
error
, and
data
.

数组中的第二个重构值是一个对象,我们可以在上面使用对象解构,并从中获得与

useQuery
相同的所有属性,例如
loading
error
data

We also get an important property named

called
, which tells us if we've actually called this function to perform our query. In that case, if
called
is true and
loading
is true, we want to return "Loading..." instead of our actual data, because are waiting for the data to be returned. This is how
useLazyQuery
handles fetching data in a synchronous way without any promises.

我们还获得命名的一个重要性质

called
,它告诉我们,如果我们实际上是调用这个函数来执行我们的查询。 在这种情况下,如果
called
为true并且
loading
为true,则我们想返回“ Loading ...”而不是我们的实际数据,因为正在等待数据被返回。 这就是
useLazyQuery
如何以同步方式处理数据获取而没有任何承诺的方式。

Note that we again pass any required variables for the query operation as a property, variables, to the second argument. However, if we need, we can pass those variables on an object provided to the query function itself.

请注意,我们再次将查询操作所需的所有变量作为属性(变量)传递给第二个参数。 但是,如果需要,我们可以将这些变量传递给提供给查询函数本身的对象。

useMutation挂钩 (useMutation Hook)

Now that we know how to execute lazy queries, we know exactly how to work with the

useMutation
hook.

既然我们知道了如何执行懒惰的查询,那么我们确切地知道了如何使用

useMutation
挂钩。

Like the

useLazyQuery
hook, it returns an array which we can destructure into its two elements. In the first element, we get back a function, which in this case, we can call it to perform our mutation operation. For next element, we can again destructure an object which returns to us
loading
,
error
and
data
.

就像

useLazyQuery
挂钩一样,它返回一个数组,我们可以将其分解为两个元素。 在第一个元素中,我们返回一个函数,在这种情况下,我们可以调用它执行我们的变异操作。 对于下一个元素,我们可以再次分解一个对象,该对象返回给我们
loading
error
data

import { useMutation } from "@apollo/react-hooks";
import { gql } from "apollo-boost";

const CREATE_POST = gql`
mutation CreatePost($title: String!, $body: String!) {
insert_posts(objects: { body: $body, title: $title }) {
affected_rows
}
}
`;

function NewPost() {
const [title, setTitle] = React.useState("");
const [body, setBody] = React.useState("");
const [createPost, { loading, error }] = useMutation(CREATE_POST);

function handleCreatePost(event) {
event.preventDefault();
// the mutate function also doesn't return a promise
createPost({ variables: { title, body } });
}

return (
<div>
<h1>New Post</h2>
<form onSubmit={handleCreatePost}>
<input onChange={(event) => setTitle(event.target.value)} />
<textarea onChange={(event) => setBody(event.target.value)} />
<button disabled={loading} type="submit">
Submit
</button>
{error && <p>{error.message}</p>}
</form>
</div>
);
}

Unlike with queries, however, we don't use

loading
or
error
in order to conditionally render something. We generally use
loading
in such situations as when we're submitting a form to prevent it being submitted multiple times, to avoid executing the same mutation needlessly (as you can see in the example above).

但是,与查询不同,我们不使用

loading
error
来有条件地渲染某些内容。 我们通常在诸如提交表单的情况下使用
loading
,以防止多次提交表单,以避免不必要地执行相同的更改(如您在上面的示例中看到的)。

We use

error
to display what goes wrong with our mutation to our users. If for example, some required values to our mutation are not provided, we can easily use that error data to conditionally render an error message within the page so the user can hopefully fix what's going wrong.

我们使用

error
向用户显示我们的突变出了什么问题。 例如,如果未提供突变所需的某些值,则我们可以轻松地使用该错误数据在页面中有条件地呈现错误消息,以便用户希望可以解决问题。

As compared to passing variables to the second argument of

useMutation
, we can access a couple of useful callbacks when certain things take place, such as when the mutation is completed and when there is an error. These callbacks are named
onCompleted
and
onError
.

与将变量传递给

useMutation
的第二个参数
useMutation
,当某些事情发生时,例如当突变完成和出现错误时,我们可以访问几个有用的回调。 这些回调分别命名为
onCompleted
onError

The

onCompleted
callback gives us access to the returned mutation data and it's very helpful to do something when the mutation is done, such as going to a different page. The
onError
callback gives us the returned error when there is a problem with the mutation and gives us other patterns for handling our errors.

onCompleted
回调使我们可以访问返回的突变数据,并且在完成突变后执行某些操作(例如转到另一个页面)非常有帮助。 当突变存在问题时,
onError
回调为我们提供了返回的错误,并为我们提供了处理错误的其他模式。

const [createPost, { loading, error }] = useMutation(CREATE_POST, {
onCompleted: (data) => console.log("Data from mutation", data),
onError: (error) => console.error("Error creating a post", error),
});

useSubscription挂钩 (useSubscription Hook)

The useSubscription hook works just like the useQuery hook.

useSubscription挂钩的工作原理与useQ​​uery挂钩相同。

useSubscription returns an object that we can destructure, that includes the same properties, loading, data, and error.

useSubscription返回一个可以分解的对象,其中包括相同的属性,加载,数据和错误。

It executes our subscription immediately when the component is rendered. This means we need to handle loading and error states, and only afterwards display/use our data.

呈现组件后,它将立即执行我们的订阅。 这意味着我们需要处理加载和错误状态,然后才显示/使用我们的数据。

import { useSubscription } from "@apollo/react-hooks";
import gql from "graphql-tag";

const GET_POST = gql`
subscription GetPost($id: uuid!) {
posts(where: { id: { _eq: $id } }) {
id
body
title
createdAt
}
}
`;

// where id comes from route params -> /post/:id
function PostPage({ id }) {
const { loading, error, data } = useSubscription(GET_POST, {
variables: { id },
// shouldResubscribe: true (default: false)
// onSubscriptionData: data => console.log('new data', data)
// fetchPolicy: 'network-only' (default: 'cache-first')
});

if (loading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;

const post = data.posts[0];

return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
}

Just like useQuery, useLazyQuery and useMutation, useSubscription accepts

variables
as a property provided on the second argument.

就像useQuery,useLazyQuery和useMutation一样,useSubscription接受

variables
作为第二个参数提供的属性。

It also accepts, however, some useful properties such as

shouldResubscribe
. This is a boolean value, which will allow our subscription to automatically resubscribe, when our props change. This is useful for when we're passing variables to our you subscription hub props that we know will change.

但是,它也接受一些有用的属性,例如

shouldResubscribe
。 这是一个布尔值,当我们的道具更改时,这将允许我们的订阅自动重新订阅。 当我们将变量传递给我们知道会改变的您的订阅中心道具时,这很有用。

Additionally, we have a callback function called

onSubscriptionData
, which enables us to call a function whenever the subscription hook receives new data. Finally, we can set the
fetchPolicy
, which defaults to 'cache-first'.

此外,我们还有一个名为

onSubscriptionData
的回调函数,该函数使我们可以在订阅挂钩接收到新数据时调用一个函数。 最后,我们可以设置
fetchPolicy
,默认为'cache-first'。

手动设置提取策略 (Manually Setting the Fetch Policy)

What can be very useful about Apollo is that it comes with its own cache, which it uses to manage the data that we query from our GraphQL endpoint.

关于Apollo的非常有用的是,它带有自己的缓存,用于管理从GraphQL端点查询的数据。

Sometimes, however, we find that due to this cache, things aren't updated in the UI in the way that we want.

但是,有时候,我们发现由于缓存的原因,UI中的内容并未按照我们想要的方式进行更新。

In many cases we don't, as in the example below, where we are editing a post on the edit page, and then after editing our post, we navigate to the home page to see it in a list of all posts, but we see the old data instead:

在许多情况下,我们不会像下面的示例那样在编辑页面上编辑帖子,然后在编辑帖子之后,导航到主页以在所有帖子列表中查看它,但是我们查看旧数据:

// route: /edit/:postId
function EditPost({ id }) {
const { loading, data } = useQuery(GET_POST, { variables: { id } });
const [title, setTitle] = React.useState(loading ? data?.posts[0].title : "");
const [body, setBody] = React.useState(loading ? data?.posts[0].body : "");
const [updatePost] = useMutation(UPDATE_POST, {
// after updating the post, we go to the home page
onCompleted: () => history.push("/"),
});

function handleUpdatePost(event) {
event.preventDefault();
updatePost({ variables: { title, body, id } });
}

return (
<form onSubmit={handleUpdatePost}>
<input
onChange={(event) => setTitle(event.target.value)}
defaultValue={title}
/>
<input
onChange={(event) => setBody(event.target.value)}
defaultValue={body}
/>
<button type="submit">Submit</button>
</form>
);
}

// route: / (homepage)
function App() {
const { loading, error, data } = useQuery(GET_POSTS, {
variables: { limit: 5 },
});

if (loading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;

// updated post not displayed, still see old data
return data.posts.map((post) => <Post key={post.id} post={post} />);
}

This not only due to the Apollo cache, but also the instructions for what data the query should fetch. We can changed how the query is fetched by using the

fetchPolicy
property.

这不仅是由于Apollo缓存,而且还取决于查询应获取哪些数据的说明。 我们可以使用

fetchPolicy
属性更改查询的获取方式。

By default, the

fetchPolicy
is set to 'cache-first'. It's going to try to look at the cache to get our data instead of getting it from the network.

默认情况下,

fetchPolicy
设置为“缓存优先”。 它将尝试查看缓存以获取我们的数据,而不是从网络获取数据。

An easy way to fix this problem of not seeing new data is to change the fetch policy. However, this approach is not ideal from a performance standpoint, because it requires making an additional request (using the cache directly does not, because it is local data).

解决此问题的简便方法是更改​​获取策略。 但是,从性能的角度来看,此方法并不理想,因为它需要发出额外的请求(因为它是本地数据,所以不需要直接使用缓存)。

There are many different options for the fetch policy listed below:

下面列出了许多用于提取策略的选项:

{
fetchPolicy: "cache-first"; // default
/*
cache-and-network
cache-first
cache-only
network-only
no-cache
standby
*/
}

I won't go into what each policy does exactly, but to solve our immediate problem, if you always want a query to get the latest data by requesting it from the network, we set

fetchPolicy
to 'network-first'.

我不会深入研究每个策略的确切作用,但是要解决我们眼前的问题,如果您始终希望查询通过从网络请求来获取最新数据,请将

fetchPolicy
设置为“网络优先”。

const { loading, error, data } = useQuery(GET_POSTS, {
variables: { limit: 5 },
fetchPolicy: "network-first"
});

发生突变时更新缓存 (Updating the cache upon a mutation)

Instead of bypassing the cache by changing the fetch policy of

useQuery
, let's attempt to fix this problem by manually updating the cache.

让我们尝试通过手动更新缓存来解决此问题,而不是通过更改

useQuery
的获取策略来
useQuery
缓存。

When performing a mutation with

useMutation
. We have access to another callback, known as
update
.

使用

useMutation
执行突变时。 我们可以访问另一个回调,称为
update

update
gives us direct access to the cache as well as the data that is returned from a successful mutation. This enables us to read a given query from the cache, take that new data and write the new data to the query, which will then update what the user sees.

update
使我们可以直接访问缓存以及成功进行突变后返回的数据。 这使我们能够从缓存中读取给定的查询,获取新数据并将新数据写入查询,然后更新用户看到的内容。

Working with the cache manually is a tricky process that a lot of people tend to avoid, but it's very helpful because it saves some time and resources by not having to perform the same request multiple times to update the cache manually.

手动处理缓存是一个棘手的过程,很多人倾向于避免这一过程,但这非常有帮助,因为它无需多次执行同一请求来手动更新缓存,从而节省了时间和资源。

function EditPost({ id }) {
const [updatePost] = useMutation(UPDATE_POST, {
update: (cache, data) => {
const { posts } = cache.readQuery(GET_POSTS);
const newPost = data.update_posts.returning;
const updatedPosts = posts.map((post) =>
post.id === id ? newPost : post
);
cache.writeQuery({ query: GET_POSTS, data: { posts: updatedPosts } });
},
onCompleted: () => history.push("/"),
});

// ...
}

We first want to read the query and get the previous data from it. Then we need to take the new data. In this case, to find the post with a given id and replace it with

newPost
data, otherwise have it be the previous data, and then write that data back to the same query, making sure that it has the same data structure as before.

我们首先要读取查询并从中获取以前的数据。 然后,我们需要获取新数据。 在这种情况下,要查找具有给定id的帖子并将其替换为

newPost
数据,否则将其作为先前的数据,然后将该数据写回到相同的查询中,并确保它具有与以前相同的数据结构。

After all this, whenever we edit a post and are navigated back to the home page, we should see that new post data.

毕竟,无论何时我们编辑帖子并导航回主页,我们都应该看到新的帖子数据。

使用useQuery重新查询 (Refetching queries with useQuery)

Let's say we display a list of posts using a

GET_POSTS
query and are deleting one of them with a
DELETE_POST
mutation.

假设我们使用

GET_POSTS
查询显示帖子列表,并删除其中一个带有
DELETE_POST
突变的
DELETE_POST

When a user deletes a post, what do we want to happen?

当用户删除帖子时,我们想发生什么?

Naturally, we want it to be removed from the list, both the data and what is displayed to the users. When a mutation is performed, however, the query doesn't know that the data is changed.

自然,我们希望将其从列表中删除,包括数据和显示给用户的内容。 但是,当执行变异时,查询不知道数据已更改。

There are a few ways of updating what we see, but one approach is to reexecute the query.

有几种方法可以更新我们看到的内容,但是一种方法是重新执行查询。

We can do so by grabbing the

refetch
function which we can destructure from the object returned by the
useQuery
hook and pass it down to the mutation to be executed when it is completed, using the
onCompleted
callback function:

我们可以抓住这样做

refetch
功能,我们可以通过返回的对象解构
useQuery
钩传下来的突变完成时,它要执行,使用
onCompleted
回调函数:

function Posts() {
const { loading, data, refetch } = useQuery(GET_POSTS);

if (loading) return <div>Loading...</div>;

return data.posts.map((post) => (
<Post key={post.id} post={post} refetch={refetch} />
));
}

function Post({ post, refetch }) {
const [deletePost] = useMutation(DELETE_POST, {
onCompleted: () => refetch(),
});

function handleDeletePost(id) {
if (window.confirm("Are you sure you want to delete this post?")) {
deletePost({ variables: { id } });
}
}

return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
<button onClick={() => handleDeletePost(post.id)}>Delete</button>
</div>
);
}

使用useMutation重新查询 (Refetching Queries with useMutation)

Note that we can also utilize the

useMutation
hook to reexecute our queries through an argument provided to the mutate function, called
refetchQueries
.

请注意,我们也可以利用

useMutation
钩通过提供给发生变异函数,称为参数重新执行我们的查询
refetchQueries

It accepts an array of queries that we want to refetch after a mutation is performed. Each queries is provided within an object, just like we would provide it to client.query(), and consists of a query property and a variables property.

它接受执行突变后要重新获取的查询数组。 每个查询都在一个对象内提供,就像我们将其提供给client.query()一样,并且由一个查询属性和一个变量属性组成。

Here is a minimal example to refetch our

GET_POSTS
query after a new post is created:

这是创建新帖子后重新获取

GET_POSTS
查询的最小示例:

function NewPost() {
const [createPost] = useMutation(CREATE_POST, {
refetchQueries: [
{
query: GET_POSTS,
variables: { limit: 5 }
}
],
});

// ...
}

将客户端与useApolloClient一起使用 (Using the client with useApolloClient)

We can get access to the client across our components with the help of a special hook called use Apollo client. This execute the hook at the top of our function component and we get back the client itself.

借助名为use Apollo client的特殊钩子,我们可以跨组件访问客户端。 这将执行我们函数组件顶部的挂钩,然后我们将返回客户端本身。

function Logout() {
const client = useApolloClient();
// client is the same as what we created with new ApolloClient()

function handleLogout() {
// handle logging out user, then clear stored data
logoutUser();
client.resetStore().then(() => console.log("logged out!"));
/* Be aware that .resetStore() is async */
}

return <button onClick={handleLogout}>Logout</button>;
}

And from there we can execute all the same queries, mutations, and subscriptions.

从那里我们可以执行所有相同的查询,变异和订阅。

Note that there are a ton more features that come with methods that come with the client. Using the client, we can also write and read data to and from the cache that Apollo sets up (using

client.readData()
and
client.writeData()
).

请注意,客户端附带的方法还提供许多其他功能。 使用客户端,我们还可以使用Apollo设置的缓存向其中写入数据和从中读取数据(使用

client.readData()
client.writeData()
)。

Working with the Apollo cache deserves its own crash course in itself. A great benefit of working with Apollo is that we can also use it as a state management system to replace solutions like Redux for our global state. If you want to learn more about using Apollo to manage global app state you can check out the following link.

使用Apollo缓存本身应该拥有自己的崩溃过程。 与Apollo合作的一大好处是,我们还可以将其用作状态管理系统,以代替Redux等解决方案来解决我们的全球状态。 如果要了解有关使用Apollo管理全局应用程序状态的更多信息,可以查看以下链接

I attempted to make this cheatsheet as comprehensive as possible, though it still leaves out many Apollo features that are worth investigating.

我尝试使该备忘单尽可能全面,尽管它仍然遗漏了许多值得研究的Apollo功能。

If you want to more about Apollo, be sure to check out the official Apollo documentation.

如果您想进一步了解Apollo,请务必查看Apollo官方文档

下载备忘单📄 (Download the cheatsheet 📄)

Want a quick reference of all of these concepts?

是否需要所有这些概念的快速参考?

Click to grab the complete PDF cheatsheet

单击以获取完整的PDF速查表

翻译自: https://www.freecodecamp.org/news/react-apollo-client-2020-tutorial/

react apollo

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: