当前位置:   article > 正文

获取当前按钮的内容_Apollo入门引导(七):通过查询获取数据

apollo java获取键值
接上篇 —— Apollo 入门引导(六):创建Apollo客户端 —— 继续翻译 Apollo 的官网入门引导 。

使用 React 的 Hook:useQuery

Apollo 入门引导 - 目录:

  1. 介绍
  2. 构建 schema
  3. 连接数据源
  4. 编写查询解析器
  5. 编写变更解析器
  6. 连接 Apollo Studio
  7. 创建 Apollo 客户端
  8. 通过查询获取数据
  9. 通过变更修改数据
  10. 管理本地状态

完成时间:20 分钟

前一节已经设置了 Apollo 客户端,现在可以将其集成到我们的 React 应用中了。可以使用React Hooks将 GraphQL 查询的结果直接绑定到 UI。

与 React 集成

为了将 Apollo 客户端连接到 React,需要将应用程序封装在 @apollo/client 包中的 ApolloProvider 组件中。我们通过 client 属性将客户端实例传递给 ApolloProvider 组件。

打开 src/index.tsx ,更新以下内容:

import {  ApolloClient,  NormalizedCacheObject,  ApolloProvider,} from '@apollo/client';import { cache } from './cache';import React from 'react';import ReactDOM from 'react-dom';import Pages from './pages';import injectStyles from './styles';// 初始化Apollo 客户端const client: ApolloClient = new ApolloClient({  cache,  uri: 'http://localhost:4000/graphql',});injectStyles();// 传递ApolloClient的实例给ApolloProvider组件ReactDOM.render(  </ApolloProvider>,  document.getElementById('root'));

ApolloProvider 组件类似于 React 的上下文提供器:它封装了 React 应用,并将 client 放置在上下文中,所以可以从组件树中的任何位置访问它。现在,准备构建执行 GraphQL 查询的 React 组件。

显示发射列表

接下来在应用中构建页面,该页面显示可获得的 SpaceX 发射的列表。打开 src / pages / launches.tsx,该文件如下所示:

import React, { Fragment, useState } from 'react';import { RouteComponentProps } from '@reach/router';import { gql } from '@apollo/client';export const LAUNCH_TILE_DATA = gql`  fragment LaunchTile on Launch {    __typename    id    isBooked    rocket {      id      name    }    mission {      name      missionPatch    }  }`;interface LaunchesProps extends RouteComponentProps {}const LaunchesReact.FC = () => {return };export default Launches;

定义查询

首先定义查询的格式,该查询将用于获取发射的分页列表。将以下内容粘贴到 LAUNCH_TILE_DATA 声明的下方:

export const GET_LAUNCHES = gql`  query GetLaunchList($afterString) {    launches(after: $after) {      cursor      hasMore      launches {        ...LaunchTile      }    }  }${LAUNCH_TILE_DATA}`;
使用片段

注意,在定义查询时,在其上方插入了 LAUNCH_TILE_DATA 的定义。LAUNCH_TILE_DATA 定义了一个 GraphQL 片段(fragment),名为 LaunchTile。片段对于定义一组字段时很有帮助,无需重写就可以将这组字段包含在多个查询中。

在上面的查询中,通过 ...LaunchTile 方式引入了片段,类似于 JavaScript spread 语法。

分页详细信息

注意,除了获取 launches 列表之外,查询还获取 hasMorecursor 字段。这是因为 launches 查询返回 分页结果

  • hasMore 字段指示服务返回的列表后面是否还有其他的发射信息。
  • cursor 字段指示客户端在发射列表中的当前位置。可以再次执行查询,并提供最新的 cursor 作为 $after 变量的值,以获取列表中的 下一批 发射的集合。

使用 useQuery hook

我们将使用 Apollo 客户端的 useQuery React Hook在 Launches 组件中执行新查询。Hook 的结果对象提供的属性可以在查询执行时填充和呈现组件。

  1. 修改 @apollo/client ,导入 useQuery,再导入一些预定义的组件以呈现页面:
import { gql, useQuery } from '@apollo/client';import { LaunchTileHeaderButtonLoading } from '../components';

如果使用的是 TypeScript,需要从服务的 schema 定义中导入必需的类型:

import * as GetLaunchListTypes from './__generated__/GetLaunchList';
  1. 将伪声明 const Launches 替换为以下内容:
const Launches: React.FC = () => {const { data, loading, error } = useQuery<    GetLaunchListTypes.GetLaunchList,    GetLaunchListTypes.GetLaunchListVariables  >(GET_LAUNCHES);if (loading) return ;if (errorreturn 

ERROR</p>;
  if (!data) return 

Not found

p>;return (
      {data.launches &&
        data.launches.launches &&
        data.launches.launches.map((launch: any) => (
        ))}
    </Fragment>
  );
};

该组件将 GET_LAUNCHES 查询传给 useQuery ,并从结果中获取 dataloadingerror 属性。根据这些属性的状态来展现发射列表,加载状态和错误信息。

使用 npm start 启动服务和客户端,并访问 localhost:3000。如果一切都配置正确,将会展现应用主页,并列出 20 次 SpaceX 发射!

但是有一个问题:总共的 SpaceX 发射数超过了 20 个。服务会分页显示其结果,并在一次响应中最多包含 20 次发射。

为了能够获取并存储 全部 启动,需要修改代码以使用查询中包含的 cursorhasMore 字段。接下来学习如何做分页支持。

添加分页支持

本教程中没有展现 Apollo client 3 为基于偏移和Relay 风格的分页助手函数(pagination helper function)。

Apollo 客户端提供了一个 fetchMore 助手函数来协助分页查询。可以用不同值的变量(例如当前游标)来执行相同的查询。

useQuery 结果对象中解构的对象列表中添加 fetchMore ,并定义一个 isLoadingMore 状态变量:

const Launches: React.FC = () => {const {    data,    loading,    error,    fetchMore, // highlight-line  } = useQuery<    GetLaunchListTypes.GetLaunchList,    GetLaunchListTypes.GetLaunchListVariables  >(GET_LAUNCHES);const [isLoadingMore, setIsLoadingMore] = useState(false); //highlight-line// ...};

现在将 fetchMore 连接到 Launches 组件中的按钮上,单击该按钮可获取其他发射。

将此代码直接粘贴在 Launches 组件的结束 Fragment> 标签上方:

{  data.launches &&    data.launches.hasMore &&    (isLoadingMore ? (          ) : (              onClick={async () => {          setIsLoadingMore(true);          await fetchMore({            variables: {              afterdata.launches.cursor,            },          });          setIsLoadingMore(false);        }}      >        Load More      </Button>    ));}//Fragment>

单击按钮时,它将调用 fetchMore (将当前的 cursor 的值传给after 变量),直到查询返回结果前一直显示加载中的状态。

启动所有内容,然后再次访问 localhost:3000 。现在已有的 20 个发射下方会出现一个 Load More 按钮,点击它。查询返回后, 没有其他发射出现。?

如果检查浏览器的网络活动,会发现该按钮确实向服务发送了后续查询,并且服务确实响应了新的发射列表。但是,Apollo 客户端将这些列表分隔开,因为它们表示带有 不同变量值 (本例为 after 的值)的查询结果。

我们需要 Apollo 客户端来将 fetchMore 查询中的发射与 之前 查询中的发射进行 合并 。接下来配置该行为。

合并缓存的结果

Apollo 客户端将查询结果存储在内存缓存中。缓存可以智能高效地处理大多数操作,但是并不能自动知道我们是否要合并两个不同的发射列表。为了解决这个问题,为 schema 中的分页字段定义一个 合并函数(merge function)

打开 src/cache.ts,现在初始化的是默认 InMemoryCache

import { InMemoryCacheReference } from '@apollo/client';export const cacheInMemoryCache = new InMemoryCache({});

服务中分页的 schema 字段是 launches 列表。修改 cache 的初始化过程,为 launches 字段添加一个 merge 函数,如下所示:

export const cache: InMemoryCache = new InMemoryCache({  typePolicies: {    Query: {      fields: {        launches: {          keyArgs: false,          merge(existing, incoming) {            let launches: Reference[] = [];            if (existing && existing.launches) {              launches = launches.concat(existing.launches);            }            if (incoming && incoming.launches) {              launches = launches.concat(incoming.launches);            }            return {              ...incoming,              launches,            };          },        },      },    },  },});

这个 merge 方法接受我们现有的缓存发射(existing)和传入的发射(incoming),并将它们组合成一个列表并返回。缓存存储了此组合列表,并将其返回给所有使用 launches 字段的查询。

此示例展示字段策略的用法,这是针对 schema 中各个字段的缓存配置选项。

如果现在尝试单击 Load More 按钮,则 UI 将成功将其他发射附加到列表中!

显示单次发射的详情

我们希望能够单击列表中的发射以查看其完整详情。打开 src/pages/launch.tsx 并替换为以下内容:

import { gql } from '@apollo/client';import { LAUNCH_TILE_DATA } from './launches';export const GET_LAUNCH_DETAILS = gql`  query LaunchDetails($launchId: ID!) {    launch(id: $launchId) {      site      rocket {        type      }      ...LaunchTile    }  }${LAUNCH_TILE_DATA}`;

该查询包含了所有详情。注意,代码复用了已经在 launches.tsx 中定义的 LAUNCH_TILE_DATA 片段。

再一次将查询传递给 useQuery hook。这次还需要将对应发射的 launchId 作为变量传给查询。launchId'的值可用路由来传递。

现在,将 launch.tsx 的内容替换为以下内容:

import React, { Fragment } from 'react'// preserve-lineimport { gql, useQuery } from '@apollo/client'// preserve-lineimport { LAUNCH_TILE_DATA } from './launches';import { Loading, Header, LaunchDetail } from '../components'// preserve-lineimport { ActionButton } from '../containers'// preserve-lineimport { RouteComponentProps } from '@reach/router';import * as LaunchDetailsTypes from './__generated__/LaunchDetails';export const GET_LAUNCH_DETAILS = gql`  query LaunchDetails($launchId: ID!) {    launch(id: $launchId) {      site      rocket {        type      }      ...LaunchTile    }  }${LAUNCH_TILE_DATA}`;interface LaunchProps extends RouteComponentProps {  launchId?: any;}const Launch: React.FC = ({ launchId }) => {const { data, loading, error } = useQuery<    LaunchDetailsTypes.LaunchDetails,    LaunchDetailsTypes.LaunchDetailsVariables  >(GET_LAUNCH_DETAILS, { variables: { launchId } });if (loading) return ;if (errorreturn 

ERROR: {error.message}</p>;
  if (!data) return 

Not found

p>;return (        image={
          data.launch && data.launch.mission && data.launch.mission.missionPatch
        }
      >
        {data && data.launch && data.launch.mission && data.launch.mission.name}
      </Header>>
    </Fragment>
  );
};
export default Launch;

像以前一样,正在查询时呈现 loadingerror 状态,在查询完成后呈现数据。

回到应用中,单击列表中的发射以查看详情页面。

显示个人资料页面

我们希望用户的个人资料页面显示其已预订的发射列表。打开 src/pages/profile.tsx 并将其内容替换为以下内容:

import React, { Fragment } from 'react'// preserve-lineimport { gql, useQuery } from '@apollo/client'// preserve-lineimport { Loading, Header, LaunchTile } from '../components'// preserve-lineimport { LAUNCH_TILE_DATA } from './launches'// preserve-lineimport { RouteComponentProps } from '@reach/router';import * as GetMyTripsTypes from './__generated__/GetMyTrips';export const GET_MY_TRIPS = gql`  query GetMyTrips {    me {      id      email      trips {        ...LaunchTile      }    }  }${LAUNCH_TILE_DATA}`;interface ProfileProps extends RouteComponentProps {}const Profile: React.FC = () => {const { data, loading, error } = useQuery(    GET_MY_TRIPS,    { fetchPolicy: 'network-only' } // highlight-line  );if (loading) return ;if (errorreturn 

ERROR: {error.message}</p>;
  if (data === undefined) return 

ERROR

p>;return (My Trips</Header>
      {data.me && data.me.trips.length ? (
        data.me.trips.map((launch: any) => (>
        ))
      ) : (

You haven't booked any trips


      )}
  );
};
export default Profile;

你应该从已经完成的页面中找到上述代码中的所有概念,只有一个例外:正在设置的 fetchPolicy

自定义 fetch 策略

如前所述,Apollo 客户端将查询结果存储在其缓存中。如果查询缓存中已存在的数据,则 Apollo 客户端会直接返回该数据,而无需通过网络获取。

但是,缓存的数据可能会过时。在大部分情况下,稍微过时的数据是可以接受的,但是用户的预订行程列表应当是时刻保持最新的。为了解决这个问题,专门为 GET_MY_TRIPS 查询指定了fetch 策略

fetch 策略定义了 Apollo 客户端如何将缓存用于特定查询。默认策略是 cache-first(缓存优先),这意味着 Apollo 客户端在发出网络请求之前会检查缓存,查看结果是否存在。如果存在结果,则不会发生网络请求。

通过将此查询的 fetch 策略设置为 network-only(仅限网络来源),就能保证 Apollo 客户端 始终 是从服务中获取用户的最新预订行程列表。

有关所有支持的 fetch 策略的列表,请参阅支持的 fetch 策略。

如果访问应用中的个人资料页面,则会发现查询返回 null。这是因为还未实现登录功能。将在下一节中解决这个问题!

c9a0e6c4aef7052f0248fe14dea45942.png


前端记事本,不定期更新,欢迎关注!

  • 微信公众号:林景宜的记事本
  • 博客:林景宜的记事本
  • 掘金专栏:林景宜的记事本
  • 知乎专栏:林景宜的记事本
  • Github: MageeLin

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/827161
推荐阅读
相关标签
  

闽ICP备14008679号