我目前正在使用ReactGraph应用程序,该应用程序使用GraphQL后端并具有其他本地状态。我正在使用解析器来解析随时间变化的本地字段,但是该解析器仅触发一次。
我尝试使用cache.readQuery来重新运行查询,以防本地字段发生更改,但它似乎无法按预期工作。
$('body.page')
在这种情况下,即使我通过突变“ selectDevice”对缓存进行了突变,解析器中的“ resolvedDevice”也只执行一次。我希望通过缓存更改本地状态时,解析器也会再次运行,因为缓存正在更改。
以下是执行查询的代码:
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
}
`;
在这个组件中,我正在selectedDevice上运行一个突变:
const ModalContainer = props => {
const { loading, error, data } = useQuery(query);
if (loading || error) {
return null;
}
return (
<Modal
device={data.resolvedDevice}
/>
);
};
答案 0 :(得分:2)
当高速缓存中的值更改时,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
现在位于缓存中,因此查询它的组件现在将在其更改时(无论何时“ upsteam” 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,因此您可能必须对钩子等方法进行调整。尽管原理应相同:通过观察对缓存中源字段的查询来定义派生字段,然后将派生的字段写回到缓存。然后,您可以基于派生字段从源数据到组件呈现ui进行反应式级联。