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;
obj:包含从父字段上的解析程序返回的结果的ROOT_QUERY对象,或者在顶级查询或突变的情况下包含对象的对象。不要太担心这个apollo-link-state。args:包含传递到字段中的所有参数的对象。例如,如果你用一个变量调用updateNetworkStatus(isConnected: true),那么args对象就是{ isConnected: true }。context:上下文对象,在您的React组件和Apollo Client网络堆栈之间共享。这里要注意的最重要的事情是,我们已经添加了Apollo cache为你的context,这样你就可以操控缓存readQuery,writeQuery,readFragment,writeFragment,和writeData。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,请查看:
apollo-link-statedocs:通过查看文档深入了解我们刚学到的概念,例如解析器和混合查询apollo-link-state。- 国家管理的未来:在
apollo-link-state公告文章中了解我们对使用GraphQL进行国家管理未来的愿景。 - Sara Vieira的教程视频:如果你想要一个关于构建应用程序的分步演练,请查看Sara Vieira的教程视频
apollo-link-state。
浙公网安备 33010602011771号