IjzerenHein / firestorter

Use Google Firestore in React with zero effort, using MobX 🤘
http://firestorter.com
MIT License
378 stars 50 forks source link

support for offline data? #9

Open cworf opened 6 years ago

cworf commented 6 years ago

firestore documentation gives the following example of how to use there built in persistence function. Though it seems like since im using mobx/firestorter this may need to be implemented in a different way. Is this a feature I can utilize using the firestorter API?

firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###',
}
);

firebase.firestore().enablePersistence()
  .then(function() {
      // Initialize Cloud Firestore through firebase
      var db = firebase.firestore();
  })
  .catch(function(err) {
      if (err.code == 'failed-precondition') {
          // Multiple tabs open, persistence can only be enabled
          // in one tab at a a time.
          // ...
      } else if (err.code == 'unimplemented') {
          // The current browser does not support all of the
          // features required to enable persistence
          // ...
      }
  });
IjzerenHein commented 6 years ago

Hey Colin, it's great than you mention this. This is an important feature to me, and I hope to get some time to implement/investigate this on short notice.

My initial thought is to allow initialisation of Firestorter with a promise that resolves to the firestore app object. And then it would work sort of like this:

async function init() {
  try {
    await firebase.firestore().enablePersistence();
  }
  catch (err) {
    if (err.code == 'failed-precondition') {
      console.warn('Multiple tabs open, persistence can only be enabled in one tab at a time.');
    } else if (err.code == 'unimplemented') {
      console.warn('The current browser does not support all of the features required to enable persistence');
    }
  }
  initFirestorter({firebase: firebase});
};
init();
IjzerenHein commented 6 years ago

Hi, regarding offline persistence, I suggest the following approach which I've been using successfully in my application for some time now. It deals with the problem in 3 phases:

  1. Render the application after initialisation has occurred
  2. Use a mobx-react Provider to create a Store and add it as a context
  3. Wrap your components with a HOC that passes in the store as a prop

1. Render the application after initialisation has occurred

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import firebase from '@firebase/app';
import '@firebase/firestore';
import { initFirestorter } from 'firestorter';
import App from './App';

firebase.initializeApp({...);

async function initFirestore() {
  firebase.firestore().settings({ timestampsInSnapshots: true });

  try {
    await firebase.firestore().enablePersistence();
  } catch (err) {
    console.error('Failed to enable firebase persistency: ', err.message);
  }

  initFirestorter({ firebase: firebase });
}

initFirestore().then(() => {
  ReactDOM.render(<App>, document.getElementById('root'));
});

2. Use a mobx-react Provider to create a Store and add it as a context

App.js

import React from 'react';
import StoreProvider from './StoreProvider';

const App = () => (
  <StoreProvider>
    <Todos />
  </StoreProvider>
);
export default App;

StoreProvider

import React, { Component } from 'react';
import { Provider } from 'mobx-react';
import Store from './Store';

export default class StoreProvider extends Component {
  state = {
    store: new Store(),
  };

  render() {
    return <Provider store={this.state.store}>{this.props.children}</Provider>;
  }
}

Store.js

import { Collection } from 'firestorter';

export default class Store {
  constructor() {
    this.todos = new Collection('todos');
    ...
  }
}

3. Wrap your components with a HOC that passes in the store as a prop

Todos.js

import React, { Component } from 'react';
import storeObserver from './storeObserver';

class Todos extends Component{
  render() {
    const { store } = this.props;
    const { todos } = store;
    return (
      <div>
        {todos.docs.map(todo => <Todo doc={todo} />)}
      </div>
    );
  }
}

// Wrap the component in a HOC which passes in the store
export default storeObserver(Todos);

storeObserver.js

import { inject, observer } from 'mobx-react';

function storeObserver(WrappedComponent) {
  return inject('store')(observer(WrappedComponent));
}

export default storeObserver;
cgadam commented 3 years ago

It seems like we're still unable to add a new document to a collection while being offline. (and let the collection decide what its auto id should look like) We can't do what is suggested here: https://github.com/firebase/firebase-js-sdk/issues/2030#issuecomment-516152577 with the current's Collection API.