apollo client学习笔记

2021-05-19  本文已影响0人  朱传武

查询用法:

import { gql, useQuery } from '@apollo/client';

const GET_DOGS = gql`
  query GetDogs {
    dogs {
      id
      breed
    }
  }
`;
function Dogs({ onDogSelected }) {
  const { loading, error, data } = useQuery(GET_DOGS);

  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>
  );
}

无需定义state, const { loading, error, data } = useQuery(GET_DOGS);即是当前的状态。

缓存查询结果

每次查询的结果都会自动缓存在内存之中,下次请求结果将用本地缓存

查询的时候携带参数

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

function DogPhoto({ breed }) {
  const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

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

刷新缓存

function DogPhoto({ breed }) {
  const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
    variables: { breed },
    pollInterval: 500,
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

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

或者调用refetch方法手动刷新:

import { NetworkStatus } from '@apollo/client';

function DogPhoto({ breed }) {
  const { loading, error, data, refetch, networkStatus } = useQuery(
    GET_DOG_PHOTO,
    {
      variables: { breed },
      notifyOnNetworkStatusChange: true,
    },
  );

  if (networkStatus === NetworkStatus.refetch) 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>
  );
}

手动获取数据

import React from 'react';
import { useLazyQuery } from '@apollo/client';

function DelayedQuery() {
  const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);

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

  return (
    <div>
      {data && data.dog && <img src={data.dog.displayImage} />}
      <button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
        Click me!
      </button>
    </div>
  );
}

获取数据的策略(是否每次从网络获取),

默认是从本地缓存获取(cache-first),要修改这个策略可以在query的时候指定:

const { loading, error, data } = useQuery(GET_DOGS, {
  fetchPolicy: "network-only"
});

支持的策略列表为:
cache-first:Apollo Client首先对缓存执行查询。如果所有请求的数据都在缓存中,则返回该数据。否则,Apollo Client将对GraphQL服务器执行查询,并在缓存数据后返回该数据。

优先级最小化应用程序发送的网络请求数量。

这是默认的获取策略。
cache-only:Apollo Client只对缓存执行查询。在这种情况下,它从不查询您的服务器。

如果缓存不包含所有请求字段的数据,则仅缓存查询将抛出错误。

cache-and-network:Apollo Client对缓存和GraphQL服务器执行完整的查询。如果服务器端查询的结果修改了缓存字段,则查询将自动更新。

提供快速响应,同时也有助于保持缓存数据与服务器数据一致
network-only:Apollo Client对GraphQL服务器执行完整的查询,而不首先检查缓存。查询的结果存储在缓存中。

优先考虑与服务器数据的一致性,但不能在缓存数据可用时提供近乎即时的响应。

no-cache:服务器请求数据本地不做缓存
standby:使用与cache-first相同的逻辑,只是此查询在基础字段值更改时不会自动更新。您仍然可以使用refetch和updateQueries手动更新该查询。

添加、更新数据

import { gql, useMutation } from '@apollo/client';

const ADD_TODO = gql`
  mutation AddTodo($type: String!) {
    addTodo(type: $type) {
      id
      type
    }
  }
`;
function AddTodo() {
  let input;
  const [addTodo, { data }] = useMutation(ADD_TODO);

  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault();
          addTodo({ variables: { type: input.value } });
          input.value = '';
        }}
      >
        <input
          ref={node => {
            input = node;
          }}
        />
        <button type="submit">Add Todo</button>
      </form>
    </div>
  );
}

如上,useMutation不会立即执行ADD_TODO,而是会返回一个函数(addTodo),这个函数需要在需要的时候手动执行,如上是在onSubmit里面执行的:

onSubmit={e => {
          e.preventDefault();
          addTodo({ variables: { type: input.value } });
          input.value = '';
        }}

更新数据

const UPDATE_TODO = gql`
  mutation UpdateTodo($id: String!, $type: String!) {
    updateTodo(id: $id, type: $type) {
      id
      type
    }
  }
`;

function Todos() {
  const { loading, error, data } = useQuery(GET_TODOS);
  const [updateTodo] = useMutation(UPDATE_TODO);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return data.todos.map(({ id, type }) => {
    let input;

    return (
      <div key={id}>
        <p>{type}</p>
        <form
          onSubmit={e => {
            e.preventDefault();
            updateTodo({ variables: { id, type: input.value } });
            input.value = '';
          }}
        >
          <input
            ref={node => {
              input = node;
            }}
          />
          <button type="submit">Update Todo</button>
        </form>
      </div>
    );
  });
}

上面onSubmit之后,服务器端数据会更新,而且本地缓存数据也会自动更新,界面会重新render.

更新多项数据

如果是列表中删除或者新增一项,本地数据不会自动更新,需要调用update方法来手动更新:

const GET_TODOS = gql`
  query GetTodos {
    todos {
      id
    }
  }
`;

function AddTodo() {
  let input;
  const [addTodo] = useMutation(ADD_TODO, {
    update(cache, { data: { addTodo } }) {
      cache.modify({
        fields: {
          todos(existingTodos = []) {
            const newTodoRef = cache.writeFragment({
              data: addTodo,
              fragment: gql`
                fragment NewTodo on Todo {
                  id
                  type
                }
              `
            });
            return [...existingTodos, newTodoRef];
          }
        }
      });
    }
  });

  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault();
          addTodo({ variables: { type: input.value } });
          input.value = "";
        }}
      >
        <input
          ref={node => {
            input = node;
          }}
        />
        <button type="submit">Add Todo</button>
      </form>
    </div>
  );
}

数据订阅

通常不要使用订阅功能,使用轮询、重新获取等方式都能满足一般需求,数据订阅基于websockt,服务器端有更新之后,会主动更新客户端数据,主要用于以下情况:
1、对大对象的微小的增量变化。反复轮询大型对象的开销很大,特别是在对象的大部分字段很少更改的情况下。相反,您可以通过查询获取对象的初始状态,并且您的服务器可以在更新发生时主动地将更新推送到各个字段。
2、低延迟的实时更新。例如,聊天应用程序的客户机希望在新消息可用时立即接收它们。
服务器端实现:

type Subscription {
  commentAdded(postID: ID!): Comment
}

客户端:

const COMMENTS_SUBSCRIPTION = gql`
  subscription OnCommentAdded($postID: ID!) {
    commentAdded(postID: $postID) {
      id
      content
    }
  }
`;

用来做restful api请求

比如我们想用它做登录操作,而登录操作是restful写好的接口,postman信息如下:


image.png
yarn add apollo-link-rest 
yarn add graphql-anywhere  -d

新建文件signin.ts

import { gql } from "@apollo/client";

export const signin = gql`
  query Signin($input: AccountInfo!) {
    signin: login(body: $input)
      @rest(
        type: "Login"
        path: "/oauth/token"
        method: "POST"
        bodyKey: "body"
      ) {
      access_token
      id_token
      scope
      expires_in
      token_type
    }
  }
`;

添加restful client

const createApolloClient = (
  token: string,
  isRest?: boolean
): ApolloClient<NormalizedCacheObject> => {
  globalToken = token;
  const links: ApolloLink[] = [
    errorLink,
    authLink(),
    // add the named link upon development
    // ...(process.env.NODE_ENV !== "production" ? [namedLink()] : []),
    httpLink,
  ];
  const restLink = new RestLink({ uri: AuthConfig.Domain });

  client = new ApolloClient({
    link: isRest ? restLink : from(links),
    cache: new InMemoryCache(),
  });
  return client;
};
const result = await restClient.query<ILoginResponseProps>({
      query: signin,
      variables: {
        input: {
          realm: "Username-Password-Authentication",
          audience: AuthConfig.Audience,
          client_id: AuthConfig.ClientId,
          scope: "openid email name profile",
          grant_type: "http://auth0.com/oauth/grant-type/password-realm",
          username: username,
          password: password,
        },
      },
    });

登录请求,这里注意


image.png

1是把返回结果加一个signin字段,2和3名字必须对应起来,

上一篇 下一篇

猜你喜欢

热点阅读