React Apollo: apollo-cache-persist seems to not be working

Maybe I misunderstood what this package does, but I assumed that it would read chached responses and help with offline application functionality.

import React from 'react'
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'

export const DATA_QUERY = gql`
    query Data {
        me {
            name
            bestFriend {
                name
            }
        }
    }
`

const options = () => ({
  fetchPolicy: 'cache-only'
})

const withData = graphql(DATA_QUERY, { options })

export const Start = ({ data }) =>
    data.loading ? (
        'loading!'
    ) : data.me ? (
        <div>
      {console.log('data', data)}
            <h3>Me: {data.me.name}</h3>
            <p>Best friend: {data.me.bestFriend.name}</p>
        </div>
    ) : (
        'no data'
    )

export default withData(Start)

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { ApolloProvider } from 'react-apollo'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import { persistCache } from 'apollo-cache-persist'

const cache = new InMemoryCache()

persistCache({
  cache,
  storage: window.localStorage,
  debug: true
})

export const client = new ApolloClient({
  link: new HttpLink({ uri: 'https://v9zqq45l3.lp.gql.zone/graphql' }),
  cache
})

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
document.getElementById('root'));
registerServiceWorker();

` I do have the cache in my localSorage

`
apollo-cache-persist: "{"$ROOT_QUERY.me":{"name":"Bob","bestFriend":{"type":"id","id`enter code here`":"$ROOT_QUERY.me.bestFriend","generated":true}"
` 

When running the above example with fetchPolicy: 'cache-only' the component renders 'no data'. If I do the default fetchPolicy, cache-first, then I get the expected result but I can see the network request is being made.

EDIT: Now works with Daniels answer and this work around to wait for cache to be restored before running the query.

import Start from './Start'

class App extends Component {
  state = {
    show: false
  }

  toggle = () =>
    this.setState({ show: !this.state.show })

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <br/><br/>
        <button onClick={this.toggle}>Show it</button>
        <br/><br/>
        {this.state.show && <Start />}
      </div>
    );
  }
}

2 answers

  • answered 2018-03-14 00:59 Daniel Rearden

    In order to correctly cache and later retrieve the data from the cache, Apollo needs an id (or _id) to work with. If you want to use a different property as the id (like name), you can pass a dataIdFromObject function to your configuration for the in-memory cache:

    const cache = new InMemoryCache({
      dataIdFromObject: object => {
        switch (object.__typename) {
          //User is whatever type "me" query resolves to
          case 'User': return object.name;
          default: return object.id || object._id;
        }
      }
    });
    

  • answered 2018-03-14 17:22 jkdowdle

    Something like this works, though I wonder if there should be a more elegant solution. Maybe the Retry Link.

    https://github.com/apollographql/apollo-cache-persist/issues?utf8=%E2%9C%93&q=is%3Aissue+

    export class Index extends Component {
        state = {
            client: null
        }
    
        async componentWillMount() {
            const httpLink = new HttpLink({ uri: 'https://v9zqq45l3.lp.gql.zone/graphql' })
    
            const link = ApolloLink.from([ httpLink ])
    
            const cache = new InMemoryCache({
                dataIdFromObject: (object) => {
                    switch (object.__typename) {
                        // User is whatever type "me" query resolves to
                        case 'User':
                            return object.name
                        default:
                            return object.id || object._id
                    }
                }
            })
    
            await persistCache({
                cache,
                storage: window.localStorage,
                debug: true
            })
    
            const client = new ApolloClient({
                link,
                cache
        })
    
        this.setState({ client })
        }
    
        render() {
            return !this.state.client ? (
                null
            ) : (
                <ApolloProvider client={this.state.client}>
                    <App />
                </ApolloProvider>
            )
        }
    }
    
    ReactDOM.render(<Index />, document.getElementById('root'))