预期结果: 子组件具有来自远程和本地状态的数据。
实际结果: 子组件将获得一个空对象。
如何重现该问题:
我们的初始查询使用apolloClient.query
方法与<Query />
组件相反。之所以这样做,是因为我们想利用原始异步JavaScript函数中的数据(React-Router v2 <Route />
getComponents
消耗了它的返回值)。当我们使用<Query />
进行初始查询时,我没有遇到这个问题。如果我在查询中排除@client
指令,则子组件将接收远程数据。如果我要将@client
查询下移到... on Content
范围内,则会收到这个奇怪的错误,我似乎找不到任何数据。 [Network error]: Error: Missing selection set for object of type haveBeenSelected returned for query field haveBeenSelected
以下代码:
// apolloClient.js
import {
ApolloClient,
gql,
HttpLink,
InMemoryCache,
IntrospectionFragmentMatcher,
ApolloLink,
withClientState,
onError
} from './apollo';
import { urls } from './API';
import introspectionQueryResultData from './fragmentTypes.json';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
});
const cache = new InMemoryCache({ fragmentMatcher });
const error = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const stateLink = withClientState({
cache,
defaults: {
tabs: {
defaultTabValue: /* some logic here */
selected: null,
haveBeenSelected: { __typename: 'haveBeenSelected' },
__typename: 'Tabs'
}
},
resolvers: {
Query: {
tabs(_, __, { cache }) {
return {};
}
},
Mutation: {
selectTab: (_, { selected }, { cache }) => {
const query = gql`
query {
tabs @client {
selected
haveBeenSelected
}
}
`;
const { tabs } = cache.readQuery({ query });
cache.writeData({
data: {
tabs: {
selected,
haveBeenSelected: {
[selected]: true,
...tabs.haveBeenSelected,
__typename: 'haveBeenSelected'
},
__typename: 'Tabs'
}
}
});
return null;
}
}
}
});
const link = new HttpLink({
uri: urls().GRAPHQL
});
const apolloClient = new ApolloClient({
cache,
link: ApolloLink.from([error, stateLink, link])
});
apolloClient.onResetStore(stateLink.writeDefaults);
export default apolloClient;
-
// initial query and parent component
const query = gql`
query Entry($url: String!) {
entry(url: $url) {
... on Page {
name
layoutId
}
... on Content {
id
name
displayTitle
description
}
}
}
`;
const someFunctionThatReturnsParentComponent = async () => {
const { data = {} } = await apolloClient.query({
query,
variables: { url: window.location.pathname }
});
const { entry } = data;
return (
<ParentComponent {...entry} />
);
}
-
// some child component that queries for local state
const query = gql`
query Entry($url: String!) {
entry(url: $url) {
tabs @client {
defaultTabValue
selected
haveBeenSelected
}
... on Content {
description
}
}
}
`;
const SELECT_TAB_MUTATION = gql`
mutation Entry($selected: String!) {
selectTab(selected: $selected) @client
}
`;
const TabsComponent = props => (
<Query query={query} variables={{ url: window.location.pathname }}>
{({
loading,
data: {
tabs: { defaultTab, selected, haveBeenSelected },
entry: { description }
}
}) => (
<Mutation mutation={SELECT_TAB_MUTATION}>
{selectTabMutation => (
<Tabs
fetching={loading}
defaultTabValue={defaultTab}
selectedTabValue={selected}
haveBeenSelected={haveBeenSelected}
shouldShowDescriptionTab={!!description}
selectTab={selected => selectTabMutation({ variables: { selected } })}
{...props}
/>
)}
</Mutation>
)}
</Query>
);
export default TabsComponent;
版本
System:
OS: macOS 10.14.1
Binaries:
Node: 10.11.0 - /usr/local/bin/node
npm: 6.4.1 - /usr/local/bin/npm
Browsers:
Chrome: 70.0.3538.77
Safari: 12.0.1
npmPackages:
apollo-cache-inmemory: ^1.3.9 => 1.3.9
apollo-client: ^2.4.5 => 2.4.5
apollo-link: ^1.2.3 => 1.2.3
apollo-link-error: ^1.1.1 => 1.1.1
apollo-link-http: ^1.5.5 => 1.5.5
apollo-link-state: ^0.4.2 => 0.4.2
react-apollo: ^2.2.1 => 2.2.4