rethinkdb / horizon

Horizon is a realtime, open-source backend for JavaScript apps.
MIT License
6.78k stars 349 forks source link

[React Native] Add support for AsyncStorage #800

Open lirbank opened 8 years ago

lirbank commented 8 years ago

Server version: 2.0.0 Client version: 2.0.0

React Native's version of localstorage is AsyncStorage, it would be great if it was supported out of the box. I spent some time trying to add it to getStorage() but I eventually ran out of time and found a possible workaround that I could do in my app code instead. My findings/potential challenges:

  1. AsyncStorage has the same method names for setting/getting (getItem, etc) as localstorage, which is good, but it is async (no shit Sherlock!) and returns a promise. So it can't be called the same way as localstorage. To unify access to all storages, the localhost and FakeStorage method return values should probably be resolved into promises as well.
  2. AsyncStorage is part of react-native npm package, which is quite big. It would be nice if horizon didn't have to explicitly depend on react-native but just use AsyncStorage if the main app has it installed, but not 100% sure how to do that.

The following app code relies on the FakeStorage mechanism in Horizon and uses AsyncStorage to persist the storage in the app.

Btw. Horizon.clearAuthTokens() does not seem to work (see code comment below), not sure if it is broken with FakeStorage or if it's something with RN.

import React, { Component } from 'react';
import { AsyncStorage } from 'react-native';
import Horizon from '@horizon/client';

const horizonOptions = {
  host: 'localhost:8181',
};

let horizon;

export class Machine extends Component {
  state = {
    currentUser: null,
    authEndpoint: '',
  };

  signOut = () => {
    AsyncStorage.removeItem('horizon-jwt').then(() => {
      // Horizon.clearAuthTokens(); is not working in RN so using connect
      // without token to get the same behavior.
      this.connect();
    });
  }

  signIn = (horizonToken) => {
    if (! horizonToken || this.state.currentUser) {
      return;
    }

    AsyncStorage.setItem('horizon-jwt', horizonToken);
    this.connect(horizonToken);
  };

  // Custom methods
  connect(horizonToken = undefined) {
    // Options
    let options = Object.assign({}, horizonOptions);
    if (horizonToken) {
      options.authType = {'token': horizonToken};
    }

    horizon = Horizon(options);

    horizon.status(e => {
      console.log('status', e);
    });

    horizon.onSocketError(e => {
      console.log('socketError', e);
    });

    // Get the OAuth endpoint
    horizon.authEndpoint('auth0').subscribe(endpoint => {
      this.setState({authEndpoint: endpoint});
    });

    // Check if user is logged in
    if (horizon.hasAuthToken()) {
      horizon.currentUser().fetch().subscribe(user => {
        this.setState({currentUser: user});
      });
    } else {
      this.setState({currentUser: null});
    }
  }

  // Lifecycle methods
  componentDidMount() {
    AsyncStorage.getItem('horizon-jwt').then(horizonToken => {
      this.connect(horizonToken);
    });
  }

  render() {
    return; //
  }
}
deontologician commented 8 years ago

This is a good idea, I wonder if we can do it in a point release since it's an interface change (but not likely one people use).

I am hoping to make 2.1 a "react-native works" release