apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.37k stars 2.66k forks source link

[apollo-boost] Cannot read property 'writeData' of undefined #3210

Closed zebapy closed 6 years ago

zebapy commented 6 years ago

I'm encountering a problem where if I don't spread context in operation.setContext, then the cache is unavailable in my client state resolver. According to the apollo boost example, this should work.

const client = new ApolloClient({
  uri: config.apiUrl,
  fetchOptions: {
    credentials: 'include'
  },
  request: operation => {
    const token = localStorage.getItem(config.tokenName);
    operation.setContext(ctx => ({
      // This seems to fix the problem. It's as if the rest of the
      // existing context is not passed but I thought it gets merged?
      ...ctx, 
      headers: {
        authorization: token ? `Bearer ${token}` : null
      }
    }));
  },
  clientState: {
    defaults: {
      isLoggedIn: false
    },
    resolvers: {
      Mutation: {
        updateAuthStatus: (_, { loggedIn, token }, ctx) => {
          const isLoggedIn = loggedIn;

          // if I dont spread ctx in the request middleware, `cache` isn't available. Instead I just get `{headers:{...}}`

          if (loggedIn === true && token) {
            window.localStorage.setItem(config.tokenName, token);
          } else if (loggedIn === false) {
            window.localStorage.removeItem(config.tokenName);
          }

          const data = {
            isLoggedIn
          };

          ctx.cache.writeData({ data });

          return null;
        }
      }
    }
  }
});

How I'm using it. authed.js helper render prop component

import React from 'react';
import { Query, Mutation } from 'react-apollo';
import { gql } from 'apollo-boost';

const authQuery = gql`
  query authQuery {
    isLoggedIn @client
  }
`;

const updateAuthStatusMutation = gql`
  mutation authStatus($loggedIn: Boolean!, $token: String) {
    updateAuthStatus(loggedIn: $loggedIn, token: $token) @client
  }
`;

const Authed = ({ children }) => (
  <Query query={authQuery}>
    {({ data }) => (
      <Mutation mutation={updateAuthStatusMutation}>
        {updateAuthStatus => {
          const setStatus = ({ loggedIn, token }) =>
            updateAuthStatus({
              variables: {
                loggedIn,
                token
              }
            });
          return children(data.isLoggedIn, setStatus);
        }}
      </Mutation>
    )}
  </Query>
);

export default Authed;

Intended outcome: cache should be available in resolver context

Actual outcome:

ApolloError.js:34 Uncaught (in promise) Error: Network error: Cannot read property 'writeData' of undefined

stack trace
ApolloError.js:34 Uncaught (in promise) Error: Network error: Cannot read property 'writeData' of undefined
    at new ApolloError (ApolloError.js:34)
    at Object.error (QueryManager.js:118)
    at SubscriptionObserver.error (zen-observable.js:178)
    at Object.error (index.js:34)
    at SubscriptionObserver.error (zen-observable.js:178)
    at SubscriptionObserver.error (zen-observable.js:178)
ApolloError @ ApolloError.js:34
error @ QueryManager.js:118
error @ zen-observable.js:178
error @ index.js:34
error @ zen-observable.js:178
error @ zen-observable.js:178
Promise.catch (async)
Mutation._this.runMutation @ react-apollo.browser.umd.js:505
setStatus @ Authed.js:22
(anonymous) @ Autologin.js:48
Promise.then (async)
componentDidMount @ Autologin.js:38
commitLifeCycles @ react-dom.development.js:8770
commitAllLifeCycles @ react-dom.development.js:9946
callCallback @ react-dom.development.js:542
d @ raven.js:412
invokeGuardedCallbackDev @ react-dom.development.js:581
invokeGuardedCallback @ react-dom.development.js:438
commitRoot @ react-dom.development.js:10050
performWorkOnRoot @ react-dom.development.js:11017
performWork @ react-dom.development.js:10967
requestWork @ react-dom.development.js:10878
scheduleWorkImpl @ react-dom.development.js:10732
scheduleWork @ react-dom.development.js:10689
scheduleTopLevelUpdate @ react-dom.development.js:11193
updateContainer @ react-dom.development.js:11231
(anonymous) @ react-dom.development.js:15226
unbatchedUpdates @ react-dom.development.js:11102
renderSubtreeIntoContainer @ react-dom.development.js:15225
render @ react-dom.development.js:15290
./src/index.js @ index.js:80
__webpack_require__ @ bootstrap d1ffb61512d6eb4b3bdd:678
fn @ bootstrap d1ffb61512d6eb4b3bdd:88
0 @ whitelistFilters.js:3
__webpack_require__ @ bootstrap d1ffb61512d6eb4b3bdd:678
./node_modules/ansi-regex/index.js.module.exports @ bootstrap d1ffb61512d6eb4b3bdd:724
(anonymous) @ bundle.js:728

How to reproduce the issue: I tried setting this up on codesandbox https://codesandbox.io/s/n55oo5zq6j

but it's not creating the same issue. I have so much other code in my app so maybe it's something with that

Version

hwillson commented 6 years ago

Ooops - the apollo-boost options listed in the README are out of date. Please refer to the main Apollo Client API docs for the proper ApolloClient constructor options. I'll get the README updated accordingly. Thanks!

hwillson commented 6 years ago

The problematic section was removed from the Apollo Boost README, and replaced with a link to the proper section in the official docs. Thanks!