Expected Behavior
I would expect to be able to write the error to the cache, but there seems to be something in the chain of things (maybe because this is declared before the client constructor???) is not letting a write to the cache persist and as there are no examples outside of just printing errors to the console, I am unsure of what can be done...
Actual Behavior
A write to the cache using cache.writeData({ data }) is successful and then things are mysterisouly rewritten to defaults after a pollInterval is set on a query...
A _simple_ reproduction
https://github.com/deltaskelta/apollo-link-iss-573
Issue Labels
EDIT: just added the reproduction of the issue, anyone able to tell what is going on here?
This isn't really an answer to the issue you encountered, but why do you need to write the errors to your global state?
I have a separate notifications system to handle things like error notifications. It might work well for you
My client setup solution looks like this
const App = props => (
<NotificationSystem>
{({ addNotification }) => (
<Authentication
onError={message => addNotification({ message, level: 'error' })}
authURL={this.props.authURL}
>
{({ clearToken, token, username }) => (
<ReduxStoreProvider
reduxReducers={this.props.reduxReducers}
addNotification={addNotification}
>
<ApolloClientProvider
graphqlURL={this.props.graphqlURL}
authenticationURL={this.props.authURL}
subscriptionURL={this.props.subscriptionURL}
addNotification={addNotification}
authToken={token}
deauthenticate={clearToken}
>
{this.props.children({
addNotification,
username,
logout: clearToken,
onError: message => addNotification({ message, level: 'error' }),
})}
</ApolloClientProvider>
</ReduxStoreProvider>
)}
</Authentication>
)}
</NotificationSystem>
)
where my ApolloClientProvider looks like
export class ApolloClientProvider extends React.Component<Props> {
private _apolloClient: ApolloClient<any>; // tslint:disable-line:no-any
public componentWillMount() {
/** Configure Apollo Client */
// Create an http link:
const httpLink = new HttpLink({
uri: this.props.graphqlURL,
fetchOptions: {
agent: new Agent({
rejectUnauthorized: false,
}),
},
});
// Create error handler link
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) => {
this.props.addNotification({
title: 'GraphQL Error',
message: `Message: ${message}, Location: ${locations}, Path: ${path}`,
level: 'error',
});
});
}
if (networkError) {
this.props.addNotification({
title: 'Network Error',
message: `${networkError}`,
level: 'error',
});
}
});
const networkLink = this.props.subscriptionURL != undefined ? split(
// Split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query) as OperationDefinitionNode;
return kind === 'OperationDefinition' && operation === 'subscription';
},
new WebSocketLink({
uri: this.props.subscriptionURL,
options: {
reconnect: true,
},
}),
httpLink,
) : httpLink;
const authAfterware = configureAuthAfterware({ deauthenticate: this.props.deauthenticate });
const authMiddleware = configureAuthMiddleware({ authToken: () => this.props.authToken });
const apolloLink = from([
authMiddleware,
errorLink,
networkLink,
] as any);// tslint:disable-line:no-any
this._apolloClient = new ApolloClient({
connectToDevTools: true,
link: authAfterware.concat(apolloLink as any) as any,// tslint:disable-line:no-any
cache: new InMemoryCache(),
});
}
public render() {
return (
<ApolloProvider client={this._apolloClient}>
{this.props.children}
</ApolloProvider>
);
}
}
I know this doesn't really answer the question, but it might be a serviceable work around for you
Yeah...a workaround like that could be done, but I kind of got into this local state thing from the article that made it sound like it could be used like redux so that is how I am trying to use it. I just don't want the added confusion of having to pass the addNotification prop down to every little component that can run a query and possibly error....
For example, how would you get at the NotificationSystem from deep inside the Apps children?
So, it appears this may be an issue with not setting __typename and/or id in the local state but I cannot be sure yet. If I put an object into the local state, does it have to have a typename and id as if it were a regular cache object? In the todos example there is a visibilityFilter which does not have a __typename but that is just a one off string and not a regular object....
If I leave out the __typename I get some errors, but what if I want a one-off object just like the string? Is that possible, or have I misunderstood something?
Yes. It definitely needs a typename and id because that is how the inMemoryCache normalizes data. Not sure why the cache doesn't throw though
@dyst5422 what do you mean by cache doesnt throw?
There is a lot going on here that is unintuitive...
A top level simple key like visibilityFilter in the todos example works.
An object mutation followed by a query works, unless a poll interval is set
If this is meant to replace redux then I should be able to put whatever arbitrary global state required into the cache
To anyone who may stumble here, it appears I misunderstood how the local state works. pollInterval is meant only for network requests.
If you are using local state, the Apollo Client and the cache should take care of updating your queries to the local state so you should never have to poll it.
Most helpful comment
This isn't really an answer to the issue you encountered, but why do you need to write the errors to your global state?
I have a separate notifications system to handle things like error notifications. It might work well for you
My client setup solution looks like this
where my ApolloClientProvider looks like
I know this doesn't really answer the question, but it might be a serviceable work around for you