21 分钟学 apollo-client 系列:修改本地 store 数据
修改本地 store 数据之前我们已经知道,我们可以在请求结束之后,通过自动执行 那么,如何在不触发请求的情况下,主动修改 apollo store 呢? 也许你会说通过 redux 的方式,dispatch 一个 action,由 reducer 来处理,但因为 apollo store 的数据存储方案,这会相当麻烦。 read & writeApollo 对此,提供了两组命令式 api 详见其文档:DataProxy 读完这两篇文档,你大概就能掌握修改 apollo store 的技巧。 不过其中还是有不少值得注意的点,下面通过代码和注释来体会: import React,{ PureComponent } from 'react'; import TodoQuery from './TodoQuery'; import TodoFragment form './TodoFragment'; import { withApollo,graphql } from 'react-apollo'; @graphql(TodoQuery) @withApollo // 通过 props 让组件可以访问 client 实例 class TodoContainer extends PureComponent { handleUpdateQuery = () => { const client = this.props.client; const variables = { id: 5 }; const data = client.readQuery({ variables,query: TodoQuery,}); const nextData = parseNextData(data); client.writeQuery({ variables,data: nextData,}); } handleUpdateFragment = () => { const client = this.props.client; const data = client.readFragment({ id: 'Todo:5',fragment: TodoFragment,fragmentName: 'todo',// fragment 的名字,必填,否则可能会 read 失败 }); const nextData = parseNextData(data); client.writeFragment({ id: 'Todo:5',}); } } 不过,还是需要注意,它们和 fetchMore 里的 updateQuery 一样,都存在静默失败和写入限制。 你可能还注意到了 read/writeFragment 时,其 id 并不是简单的 在此处,你只需要知道,这里 id 的值应当与你在创建 apollo client 时设置的 const { id,__typename } = data; const id = client.dataIdFromObject({ id,__typename,}); 简化接口不过你不觉得上面这种写法相当麻烦吗? 虽然先 read 再 write 比较原子化,但是考虑到大部分场景下我们只需要 update 就可以了,参数这么传来传去相当麻烦,更不用说会写太多重复的代码。
import client from './client'; function updateFragment(config) { const { id: rawId,typename,fragment,fragmentName,variables,resolver } = config; // 默认使用 fragmentName 作为 __typename const __typename = typename || toUpperHeader(fragmentName); const id = client.dataIdFromObject({ id: rawId,}); const data = client.readFragment({ id,variables }); const nextData = resolver(data); client.writeFragment({ id,}); return nextData; } function updateQuery(config) { const { variables,query,resolver } = config; const data = client.readQuery({ variables,query }); const nextData = resolver(data); client.writeQuery({ variables,}); return nextData; }; function toUpperHeader(s = '') { const [first = '',...rest] = s; return [first.toUpperCase(),...rest].join(''); } 如此,我们可以这样简化之前的代码 import React,graphql } from 'react-apollo'; import { updateQuery,updateFragment } from '@/apollo/enhancers'; @graphql(TodoQuery) @withApollo // 通过 props 让组件可以访问 client 实例 class TodoContainer extends PureComponent { handleUpdateQuery = () => { return updateQuery({ variables: { id: 5 },resolver: data => parseNextData(data),}); } handleUpdateFragment = () => { return updateFragment({ id: 5,typename: 'Todo',resolver: data => parseNextData(data); }); } } 其中, 注册为 client api在 封装修改 client 的 api 里我们提到可以使用 enhancer 的方式为 client 添加接口,如果你懒得每次都 import 这两个函数,那么不妨将他们注册为 client 的实例方法:
const enhancers = [ updateFragmentFactory,updateQueryFactory,]; export default function applyEnhancers(client) { // 更函数式的写法是把 enhancers 也作为参数传进来,但是我需要的 enhancer 比较少,做此精简 return enhancers.reduce( (result,enhancer) => enhancer(result),client ); } // --- enhancers --- function updateFragmentFactory(client) { return function updateFragment(config) { // ... } return client; } function updateQueryFactory(client) { return function updateQuery(config) { // ... }; return client; } 这样你就可以直接从 client 实例中访问这两个函数了。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |