Apollo 客户端解析器仅触发一次
Posted
技术标签:
【中文标题】Apollo 客户端解析器仅触发一次【英文标题】:Apollo Client resolver only triggers once 【发布时间】:2020-01-31 18:17:44 【问题描述】:我目前正在开发一个反应应用程序,它使用 GraphQL 后端并具有额外的本地状态。我正在使用解析器来解析随时间变化的本地字段,但解析器只触发一次。
我尝试使用 cache.readQuery 重新运行查询以防本地字段发生更改,但它似乎没有按我的预期工作。
export const resolvers =
Query:
resolvedDevice: (obj, args, cache , info) =>
const data = cache.readQuery(
query: gql`
query
selectedDevice @client
`
);
// do stuff with the data
,
Mutation:
selectDevice: (_, id , cache ) =>
cache.writeData( data: selectedDevice: id );
;
const query = gql`
query GetResolvedDevice
resolvedDevice @client
`;
在这种情况下,解析器中的“resolvedDevice”只执行一次,即使我通过突变“selectDevice”对缓存进行了突变。我希望通过突变更改本地状态时,解析器也会再次运行,因为缓存正在更改。
这是执行查询的代码:
const ModalContainer = props =>
const loading, error, data = useQuery(query);
if (loading || error)
return null;
return (
<Modal
device=data.resolvedDevice
/>
);
;
在这个组件中,我在 selectedDevice 上运行一个突变:
export const SELECT_DEVICE = gql`
mutation SelectDevice($id: String!)
selectDevice(id: $id) @client
`;
const DevicesNoGeoContainer = () =>
const [selectDevice] = useMutation(SELECT_DEVICE);
return (
<DevicesNoGeo
onGeoClick=id =>
selectDevice( variables: id );
/>
);
;
【问题讨论】:
查询不会自行自动更新。您可以在运行查询/突变的地方发布代码吗?需要明确的是,所涉及的查询都是本地的,还是您从后端获取数据? 这有帮助吗? 【参考方案1】:当缓存中的值发生变化时,Apollo 知道更新从缓存中提取字段的观察查询。在这种情况下,查询中的字段由本地解析器代替。这意味着 Apollo 没有缓存条目来订阅和响应该特定查询。因此,第一个查询已完成,除非您在挂钩结果中使用 refetch
显式触发它,否则您将不会获得任何查询更新。
我们希望解决此问题的一种方法是在缓存中“持久化”派生字段并在组件查询中使用缓存实现字段。我们可以通过显式观察源字段 (selectedDevice
) 并在处理程序中将派生字段 (resolvedDevice
) 写回缓存来做到这一点(我将继续使用您的字段名称,尽管您可能会考虑重命名它如果你走这条路线,因为它似乎是以它的定义方式命名的)。
概念验证
export const resolvers =
Mutation:
selectDevice: (_, id , cache ) =>
cache.writeData( data: selectedDevice: id );
;
const client = new ApolloClient(
resolvers
);
const sourceQuery = gql`
query
selectedDevice @client
`;
// watch the source field query and write resolvedDevice back to the cache at top-level
client.watchQuery( query: sourceQuery ).subscribe(value =>
client.writeData( data: resolvedDevice: doStuffWithTheData(value.data.selectedDevice) );
const query = gql`
query GetResolvedDevice
resolvedDevice @client
`;
因为传递给watchQuery
的查询中的字段位于缓存中,所以每次更改都会调用您的处理程序,并且我们会将派生字段写入缓存作为响应。
而且因为resolvedDevice
现在存在于缓存中,所以查询它的组件现在将在它发生变化时获得更新(即每当“upteam”selectedDevice
字段发生变化时)。
现在您可能不想真正将源字段监视查询放在顶层,因为无论您是否使用渲染组件,它都会在您的应用程序启动时运行并监视。如果您对一堆本地状态字段采用这种方法,这将特别糟糕。 我们正在开发一种方法,您可以在其中以声明方式定义派生字段及其实现功能:
export const derivedFields:
resolvedDevice:
fulfill: () => client.watchQuery( query: sourceQuery ).subscribe(value =>
client.writeData( data: resolvedDevice: doStuffWithTheData(value.data.selectedDevice),
;
然后使用 HOC 让他们参与进来:
import derivedFields from './the-file-above';
export const withResolvedField = field => RenderingComponent =>
return class ResolvedFieldWatcher extends Component
componentDidMount()
this.subscription = derivedFields[field].fulfill();
componentDidUnmount()
// I don't think this is actually how you unsubscribe, but there's
// some way to do it
this.subscription.cancel();
render()
return (
<RenderingComponent ...this.props />
);
;
;
最后包装你的模态容器:
export default withDerivedField('resolvedDevice')(ModalContainer);
请注意,我在这里的结尾是非常假设的,我只是输入它而不是拉下我们的实际代码。我们也回到了 Apollo 2.5 和 React 2.6,所以你可能不得不调整钩子等方法。但原则应该是相同的:通过查看缓存中源字段的查询来定义派生字段 em>,并将派生字段写回缓存。然后你有一个从源数据到基于派生字段的组件渲染 UI 的反应级联。
【讨论】:
抱歉耽搁了这么久。非常感谢您的回答,非常有帮助。以上是关于Apollo 客户端解析器仅触发一次的主要内容,如果未能解决你的问题,请参考以下文章
Apollo GitHunt-React:updateCommentsQuery?
在客户端解析器中导入类型时,如何避免使用 Apollo 客户端和“graphql-codegen”的角度项目中的循环依赖?