Apollo学习笔记(4)state管理

1、apollo-link-state:

Apollo缓存成为客户端应用程序中所有数据的唯一真实来源。apollo-link-state允许您将本地数据与远程数据一起存储在Apollo缓存中。要访问本地数据,只需使用GraphQL查询即可。您甚至可以在同一查询中请求本地和服务器数据!

配置apollo-link-state:

apollo-link-state已经包含在Apollo Boost中,因此您无需安装它。它可以在clientStateApollo Boost构造函数属性上配置

示例:

import ApolloClient from "apollo-boost";
import { defaults, resolvers } from "./resolvers";

const client = new ApolloClient({
  uri: `https://nx9zvp49q7.lp.gql.zone/graphql`,
  clientState: {
    defaults,
    resolvers,
    typeDefs
  }
});

  clientState包含三个参数:

defauts:是客户端初始化所需要的数据。

resolvers:GraphQL查询和修改调用的函数映射,以便读取和写入缓存

typeDefs:string | Array,代表客户端的shcema,这个schema不是用于验证,而是用于Apollo DevTools中的内省。

这些选项都不是必需的。如果您没有指定任何内容,您仍然可以使用该@client指令来查询缓存。

 2、更新本地数据

有两种方式更新缓存:

1、直接调用cache.writeData在Query或者ApolloConsumer组件中。直接写入非常适用于不依赖于当前缓存中的数据的一次性突变,例如写入单个值。

2、第二种是创建一个Mutation组件,使用mutation调用一个客户端的解析器。如果您的修改依赖于缓存中的现有值,我们建议使用解析器,例如将项添加到列表或切换布尔值。

直接修改类似于:setState;第二种修改类似于使用Redux.

(1)直接写入:

直接写入不需要Mutation和解析器(resolver).直接通过client属性的writeData写入数据。

import React from 'react';
import { ApolloConsumer } from 'react-apollo';
import Link from './Link';

const FilterLink = ({ filter, children }) => (
  <ApolloConsumer>
    {client => (
      <Link
        onClick={() => client.writeData({ data: { visibilityFilter: filter } })}
      >
        {children}
      </Link>
    )}
  </ApolloConsumer>
);

  注意直接写入并不是作为GraphQL mutation实现的,因此不应将它们包含在模式中。它们也不会验证您写入缓存的数据是否为有效GraphQL数据的形式。如果这些功能对您很重要,则应选择解析器。

如何订阅写入的数据,以便更新ui

示例:

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

import Link from './Link';

const GET_VISIBILITY_FILTER = gql`
  {
    visibilityFilter @client
  }
`;

// Remember to set a initial value for visibilityFilter with defaults
const FilterLink = ({ filter, children }) => (
  <Query query={GET_VISIBILITY_FILTER}>
    {({ data, client }) => (
      <Link
        onClick={() => client.writeData({ data: { visibilityFilter: filter } })}
        active={data.visibilityFilter === filter}
      >
        {children}
      </Link>
    )}
  </Query>
);

  在Link上增加一个active属性,如果当前的fiter和缓存中的visibilityFilter相同,则active等于true.要立即订阅客户端修改,请将其包装在Query组件而不是ApolloConsumer组件中.

注意:在查询visibilityFilter字段后跟@client,标示要从缓存中去数据而不是服务器端取数据。一旦调用了client.writeData,Query将自动更新修改的ui.所有缓存写入和读取都是同步的,因此您不必担心加载状态.

(2)通过Mutation和解析器修改

这里的解析器和服务器端的解析器是完全一样的。针对每一个type和字段进行解析。解析器一般接受四个参数:
fieldName: (obj, args, context, info) => result;

  

  1. obj:包含从父字段上的解析程序返回的结果的ROOT_QUERY对象,或者在顶级查询或突变的情况下包含对象的对象。不要太担心这个apollo-link-state
  2. args:包含传递到字段中的所有参数的对象。例如,如果你用一个变量调用updateNetworkStatus(isConnected: true),那么args对象就是{ isConnected: true }
  3. context:上下文对象,在您的React组件和Apollo Client网络堆栈之间共享。这里要注意的最重要的事情是,我们已经添加了Apollo cache为你的context,这样你就可以操控缓存readQuerywriteQueryreadFragmentwriteFragment,和writeData
  4. info:有关查询的执行状态的信息。你可能永远不必使用这个。

解析器举例:切换todo的完成状态。

export const resolvers = {
  Mutation: {
    toggleTodo: (_, variables, { cache, getCacheKey }) => {
      const id = getCacheKey({ __typename: 'TodoItem', id: variables.id })
      const fragment = gql`
        fragment completeTodo on TodoItem {
          completed
        }
      `;
      const todo = cache.readFragment({ fragment, id });
      const data = { ...todo, completed: !todo.completed };
      cache.writeData({ id, data });
      return null;
    },
  },
};

  第一步:找出todo的完成状态,我们使用cache.readFragment来完成,他有两个参数fragement和缓存id。getCacheKey上下文中的内容并传入项目__typename和来获取缓存键id

第二步:一旦我们读取了片段,我们就切换todo的完成状态并将更新的数据写回缓存。由于我们不打算在UI中使用变异的返回结果,因此我们返回null,因为默认情况下所有GraphQL类型都可以为空。

让我们使用Mutation来修改todo的状态,示例:

import React from 'react';
import { Mutation } from 'react-apollo';
import gql from 'graphql-tag';

const TOGGLE_TODO = gql`
  mutation ToggleTodo($id: Int!) {
    toggleTodo(id: $id) @client
  }
`;

const Todo = ({ id, completed, text }) => (
  <Mutation mutation={TOGGLE_TODO} variables={{ id }}>
    {toggleTodo => (
      <li
        onClick={toggleTodo}
        style={{
          textDecoration: completed ? 'line-through' : 'none',
        }}
      >
        {text}
      </li>
    )}
  </Mutation>
);

  3、查询本地数据。

查询Apollo缓存类似于查询GraphQL服务器。唯一的区别是您@client在本地字段上添加了一个指令,以指示它们应该从缓存中解析。

示例:

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

import Todo from './Todo';

const GET_TODOS = gql`
  {
    todos @client {
      id
      completed
      text
    }
    visibilityFilter @client
  }
`;

const TodoList = () => (
  <Query query={GET_TODOS}>
    {({ data: { todos, visibilityFilter } }) => (
      <ul>
        {getVisibleTodos(todos, visibilityFilter).map(todo => (
          <Todo key={todo.id} {...todo} />
        ))}
      </ul>
    )}
  </Query>
);

  由于Qurey挂载便显示,如果cache中没有查询到相应的数据,就会报错,这时我们需要在查询之前将默认值写入缓存,以防止出错。

4、默认值。

您的defaults对象表示您要写入缓存的初始状态。为客户端查询提供默认值很重要,否则如果在运行之前没有发生突变,它们可能会出错。初始状态的形状应与您计划在应用程序中查询它的方式相匹配。

const defaults = {
  todos: [],
  visibilityFilter: 'SHOW_ALL',
};

  

5、客户端架构

const typeDefs = `
  type Todo {
    id: Int!
    text: String!
    completed: Boolean!
  }

  type Mutation {
    addTodo(text: String!): Todo
    toggleTodo(id: Int!): Todo
  }

  type Query {
    visibilityFilter: String
    todos: [Todo]
  }
`;

  不是用于验证,客户端模式用于Apollo DevTools中的内省

 6、结合本地数据和远程数据。

示例:

const GET_DOG = gql`
  query GetDogByBreed($breed: String!) {
    dog(breed: $breed) {
      images {
        url
        id
        isLiked @client
      }
    }
  }
`;

  向服务器请求images,本地增加一个isLIked state.我们需要为isLiked我们的客户端字段提供初始状态,但我们在哪里放置它?我们可以指定一个解析器Image,只有在图像第一次从服务器返回时才会调用。

const resolvers = {
  Image: {
    isLiked: () => false
  }
};

  现在我们已经以解析器的形式指定了初始状态,我们可以像往常一样查询数据。如果您想切换isLiked字段,可以创建类似于toggleTodo我们在前一个示例中创建的突变的突变。

const Detail = ({ match: { params: { breed, id } } }) => (
  <View style={styles.container}>
    <Query query={GET_DOG} variables={{ breed }}>
      {({ loading, error, data }) => {
        if (loading) return <Fetching />;
        if (error) return <Error />;

        return (
          <DogList
            data={data.dog.images}
            renderRow={(type, data) => (
              <DogWithLikes
                id={id}
                isLiked={data.isLiked}
                imageId={data.id}
                url={data.url}
              />
            )}
          />
        );
      }}
    </Query>
  </View>
);

  7、下一步:

使用Apollo Client管理本地数据可以简化您的状态管理代码,因为Apollo缓存是您应用程序中所有数据的唯一真实来源。如果您想了解更多信息apollo-link-state,请查看:



posted @ 2018-08-25 17:52  tutu_python  阅读(393)  评论(0)    收藏  举报