FirebaseExtended / reactfire

Hooks, Context Providers, and Components that make it easy to interact with Firebase.
https://firebaseopensource.com/projects/firebaseextended/reactfire/
MIT License
3.54k stars 403 forks source link

What is the plan for moving to ES6? #38

Closed zoomclub closed 6 years ago

zoomclub commented 9 years ago

React is phasing out mixins with the change to ES6 classes. What is the plan for the reactfire mixin with this coming change? Could reactfire also tie in with react contexts? Below is a link to the context approach:

https://blog.jscrambler.com/react-js-communication-between-components-with-contexts

React relay with graphql is another approach coming to react. It would seem like reactfire updated to work with ES6 classes and the context approach might be a powerful alternative to relay with added collaboration! It might also work to incorporate some of the graphql as well?

jwngr commented 9 years ago

No plans at the moment, although I'd gladly comment on any PRs you send my way. We've been discussing how to get Firebase working with React Native and I think this also ties into that a little bit. Do you have any concrete ideas here?

zoomclub commented 9 years ago

Great to learn about the discussion on Firebase and RN integration! In terms of component development I'm just looking for the optimal way for Firebase to provide the data and then for editing that data on a component, in light of the rapid progress being made with ES6 and React.

There are a number of proposals out there (the ones I mentioned) and I'm considering them in the context of the app I'm engineering. I have an app with more than just a couple gears to it so it provides the dynamic to work with, I'll be in touch with what I resolve.

andyfen commented 9 years ago

Hi, any development on how to use mixins: [ReactFireMixin],

in ES6? for a react project not react native. thanks.

jwngr commented 9 years ago

There are still no active plans to update ReactFire for ES6 / React Native. You may want to check out re-base (https://github.com/tylermcginnis/re-base) which may fulfill your needs.

pandaiolo commented 9 years ago

+1

rstormsf commented 9 years ago

+1

juancarlosfarah commented 9 years ago

+1

dlrice commented 9 years ago

+1

ghost commented 9 years ago

React is phasing out mix-ins with the change to ES6 classes.

The React team chose not to allow the existing mix-in pattern when creating a React component the ES6 way because JS doesn't currently have a standard method for doing mix-ins. So React.createClass and existing mix-in usage for components probably won't go anywhere until JS has an approved spec for mix-ins. Why not take the wait and see approach rather than trying to blaze that trail?

The nice thing is you can mix both ES6 class and createClass approaches for creating React components in your apps, and simply prefer one over the other until the path forward becomes evident.

HighOnDrive commented 9 years ago

There are new frameworks like Cycle.js than need a Firebase driver too. The driver is their specific way of adding a service to Cycle.js.

IDEA! Instead of Firebase always making a specific package per given framework why not create a core dropin service and then a set of plugins for specific things like Relay, Falcor, etc. Actually, Relay and Falcor are less robust copycats of Firebase any ways and the core aspects of Firebase should remain the main focus.

Surely, it is not the best to always have to completely customize per framework. Would it not be better for Firebase to instead provide a ES6 module with the essential core features available as higher order functions and then provide plugins for the rest?

This could always be done using the existing Firebase API but there is still a lot of re-inventing the wheel per project that way. To me Firebase stands on its own as a global service to XYZ framework components and the bridge into the specific component architecture of a given framework should be as thin as possible.

It is to be expected that any savvy framework can work with ES6 modules and a library of higher order functions. While a framework may come and go Firebase does not!

jwngr commented 9 years ago

Not a bad idea @HighOnDrive. I know you'd really like to see a Cycle.js driver, but we just don't have the resources right now to investigate and develop one. We'd love to see something come out of the community though!

As for your idea about creating an ES6 module with core features, I think that is compelling. For example, it certainly would be nice to not have to recreate the realtime array logic for every new binding. However, with our core product, we need to be more cautious for many reasons: a lot more people use it, a lot more people abuse it, and we support a lot of legacy browsers. With things like ReactFire, we can be more aggressive, dropping support for old browsers and providing APIs which people may (unintentionally) abuse. It's a tough line to tread and in order for us to move it into the core SDK, we need to be very sure of it.

I appreciate the discussion here and I hope it continues. I'd love to hear more about your ideas here!

rstormsf commented 9 years ago

resources can be solved by hiring :-)

ghost commented 8 years ago

Parse maintains a similar mixin for connecting to React, but also provides a method for using ES6 classes for use with observables a la RxJS and the related React proposal. Is that something which seems worthwhile? Regardless users can still leverage REST and Flux, and leverage the new Firebase CLI tools for Node while building.

nelix commented 8 years ago

I had a little go at designing a fresh firebase API as a higher order component. This is what I came up with.

const ref = new FirebaseEndpoint('https://myapp.firebaseio.com');

@connectFirebase((props) => {
  return { // this allows us to reconfigure when out targets props change
          // if needed this could also prolly return a promise
    endpoint: ref, // should allow passing this via this.context too
    keys: {
      user: '/users/$userid',
      messages: `/rooms/${props.roomId}/messages`,
    },
    paths: {
      '/users/$userid': null,
      `/rooms/${props.roomId}/messages`: {
        isArray: true,
        query: {
          limitToLast: 10,
        },
      },
    },
  };
}, (keys, states, actions) => {
  return { // this object becomes assigned to the wrapped components props
   // we can also require more complicated actions and state reducers here and mix them in in more complex apps
    user: states[keys.user],
    messages: states[keys.messages],
    updateUser: ({name}) => actions[keys.user].set({name}), // firebase will automatically trigger a change even right away and keep the props update
  };
})
class App extends Component {
  render() {
    const {updateUser, user} = this.props;

    return (
      <div>
        Hi <input value={user.firstname} onChange={(evt) => updateUser(evt.target.value)} type="text"/>
      </div>
    );
  }
}
HighOnDrive commented 8 years ago

We need a RxJS module for Firebase that is not tied into any specific framework. The same module could then work with all the frameworks that have already moved to RxJS. When is Firebase going to quit sitting on their asses about this and get with the program!?

I mean it's a new day now with FRP in the picture, just check out what happened at the Reactive 2015 conference or the RxJS videos being presented from Angular2 events, etc, etc. etc, etc.

Cycle.js also made a real dent in the universe at Reactive 2015, here is an article in case anyone wants a jump start on the future: https://medium.com/@fkrautwald/plug-and-play-all-your-observable-streams-with-cycle-js-e543fc287872

remojansen commented 8 years ago

:+1: GraphQL & RxJS would be awesome!

kulakowka commented 8 years ago

It can help to mixin into react es6 component https://github.com/brigand/react-mixin

import React, { Component } from 'react'
import Firebase from 'firebase'
import ReactFireMixin from 'reactfire'
import reactMixin from 'react-mixin'

const ref = new Firebase('https://<APPNAME>.firebaseio.com/users')

class UsersList extends Component {
  constructor (props, context) {
    super(props, context)
    this.state = {
      users: []
    }
  }

  componentDidMount () {
    this.bindAsArray(ref, 'users')
  }

  renderUser (user) {
    return <p>{user.name}</p>
  }

  render () {    
    return (
      <div>
        {this.state.languages.map(this.renderLanguage)}    
      </div>
    )
  }
}

reactMixin(UsersList.prototype, ReactFireMixin)

export default UsersList

I created gist. You can fork it here: https://gist.github.com/kulakowka/24bb83775358ad4c3bc7

rbarman commented 8 years ago
ghost commented 8 years ago

ES6 is dead. Long live COBOL!

katowulf commented 8 years ago

image

katowulf commented 8 years ago

Jacob's answer continues to be the latest and greatest. Posts adding value on how devs can get up to speed on ES 6 or working toward a PR are welcome. Posts asking for release dates or discussing other features will be pruned. Please keep discussion on topic and productive.

fdidron commented 8 years ago

Hello

Here is a proposal for a PR. Provide a single method à la redux "connect" that provides the current mixin methods to the component class. EDIT: The terminology seems to be a High Order Component Usage would look like:

import { Component } from 'react';
import reactFire from 'reactfire';

class MyComponent extends Component {

}

export default reactFire(MyComponent);

//OR ES7 decorator style

@reactFire()
export default class MyComponent extends Component {

}
fdidron commented 8 years ago

So here is a first shot at it https://gist.github.com/fdidron/bde084f08ce64025ffba4f57c4eb441a

There is an API breaking change, since it's using a high order component, everything happens in your component's props. So you access ReactFire methods via props : this.props.bindAsArray , ... And the binded data can be found in the props of your component instead of the state.

davideast commented 8 years ago

@fdidron I love the decorator idea. With many devs using Babel this could be a nice syntactic gain that still achieves the same idea as the mixin.

bigblind commented 8 years ago

I'd like to propose a createFirebaseContainer function (or possibly just createContainer, which would work as follows:

createFirebaseContainer(component, refs);

Arguments

If refs is a function, it should take in the props that were passed to the container when rendered, and returns a FirebaseRefs object.

The FirebaseRefs object

A FirebaseRefs object, is an object that looks like this:

{
  "<propName>": {
    "ref": DatabaseRef,
    "type": "array" | "object",
  },
  ...
}

Here, "<propname>" is the name of a property you want to receive. ref should be the database reference you want to bind that property to. typeshould be either "array" or "object" depending on whether you want to bind to that reference as an array or an object.

Example Usage

Here's an example for a TodoList component:


const TodoList = React.createClass({
  render(){
    return <ul>
      {this.props.todos.map((todo) => {
        return <li><input type="checkbox" defaultChecked={todo.done}> {todo.text}</li>
      })}
    </ul>
  }
});

export default createFirebaseContainer(TodoList, {
  todos:  {
    ref: firebase.database().ref("/todos"),
    type: "array"
  }
});

Now, when you render the exported component, it would receive a todos prop, which represents the value in the database at /todos. When the value changes, the component is automatically re-rendered.

Using a function for refs

You may not know in advance where the data you need lives. For example, a TodoList will likely only want to show todos for a specific user. These items could live at /todos/{userID}. To make it easier to specify the refs at runtime, you can pass a function like this:

const TodoList = function(props) {
  return <ul>
    {props.todos.map((todo) => {
      return <li><input type="checkbox" defaultChecked={todo.done}> {todo.text}</li>
    })}
  </ul>
};

export default createFirebaseContainer(TodoList, (props) => {
  return {
    todos:  {
      ref: firebase.database().ref("/todos/" + props.userID),
      type: "array"
    }
  }
});

Now, assuming we import the exported component as TodoList, we can render it as <TodoList userID="123abc" />, and the todos for that user would be fetched.

This function could be returned as an attribute of ReactFireMixin. This way, the API is backwards compatible. The wrapper component could also make use of ReactFireMixin internally, for a long as React supports mixins using createClass.

jwngr commented 8 years ago

Thanks @fdidron and @bigblind for the proposals. I like both of them! @fdidron's is nice since it keeps practically the same API but just allows you to use it via props instead of via methods on the component itself. @bigblind is a bigger breaking change as it rethinks how you specify what nodes you are binding to.

One question I have about @bigblind's proposal is how you handle unbinding and re-binding the same reference. For example, if you have an app which displays the last ten items and then you want to now display only five, how would you handle that? I'd imagine the wrapped component's state would contain the limit value. But then the outer createFirebaseContainer() wrapper HOC won't have access to that value. So now you have to be using something like Redux to store that state in a place that the HOC can access it.

I also think we need to consider the fact that Firebase is now much more than just a Realtime Database. It also has Authentication (for signing in and managing users) and Storage (for storing files and blobs). I want to make sure the new version of ReactFire isn't so Database-centric.

I think I still need to think on these proposals a bit more and prototype some things with both. If anyone else out there has thoughts or opinions or other proposals, please join this thread.

marcello3d commented 8 years ago

A while ago I forked reactfire (pre-3.0) to allow for bind to be called multiple times on the same component: https://github.com/marcello3d/reactfire/compare/master...marcello3d:rebind

This made it really easy to do the following:

componentWillMount () {
  this._bind(this.props)
},
componentWillReceiveProps (nextProps) {
  this._bind(nextProps)
},
_bind ({ id }) {
  this.bindAsObject(root.child('users').child(id), 'user')
}

It made my logic a lot more declarative and easier to reason about.

It's implemented by unbinding and rebinding when binding to a new ref.

(The fork also treats bindAsArray as null instead of [] so you can distinguish between "not loaded yet" and an empty array—though the implementation is crude.)

bigblind commented 8 years ago

@jwngr The container that is returned by createContainer has componentWillMount and componentWillUnmount methods to handle binding/unbinding. What we can do, is apart from the props specified by the caller, also pass in a firebase object, which has methods for unbinding.

With regards to how the specific refs can change, for things like changing the number of objects returned, or changing the sort order, if you pass a function as the refs parameter, it will be re-called anytime the props change. So, fo let's say I have the following code:


const TodoList = function(props) {
  return <ul>
    {props.todos.map((todo) => {
      return <li><input type="checkbox" defaultChecked={todo.done}> {todo.text}</li>
    })}
  </ul>
};

export default createFirebaseContainer(TodoList, (props) => {
  return {
    todos:  {
      ref: firebase.database().ref("/todos/" + props.userID).limitToFirst(props.maxTodos),
      type: "array"
    }
  }
});

if you import this component as UserTodos, you can render it as: <UserTodos userID="abc", maxTodos={10} />.

If you need to store the limit, or other query data somewhere in component state, you can wrap the container in another component that keeps this data in state, and passes it as properties to the container.

Regarding auth, you could add a .auth path, which is caught by the client, and instead of binding to it in the database, it binds to the auth event listener. Or, you can have an FirebaseAuthContainer, which passes auth data as props to its children.

fdidron commented 8 years ago

Here is another idea :)

I think it would be nice to have reactfire provide a JSX component allowing Firebase binding. NOTE: I'm not saying this should be the only way to bind !

Props

const eventTypes = [ 'value', 'child_added', 'child_changed', 'child_removed', 'child_moved'];

FireAct.defaultProps = {
  eventType: 'value'
}

FireAct.propTypes = {
  bindTo: PropTypes.any,
  children: PropTypes.element,
  endAt: PropTypes.any,
  equalTo: PropTypes.any,
  eventType: PropTypes.oneOf(eventTypes).isRequired,
  limitToFirst: PropTypes.number,
  limitToLast: PropTypes.number,
  once: PropTypes.bool,
  onDataChange: PropTypes.func,
  orderByChild: PropTypes.string,
  orderByKey: PropTypes.bool,
  orderByValue: PropTypes.bool,
  orderByPriority: PropTypes.bool,
  push: PropTypes.bool,
  startAt: PropTypes.any,
  url: PropTypes.string.isRequired
}

On [➤]

Listen for data changes at a particular location.

import React from 'react';
import FireAct from 'fireact';

class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null
    }
  }

  dataChangeHandler(data) {
    //Code to handle data change (update the state for example).
  }

  render() {
    return (
      <FireAct
        onDataChange={this.dataChangeHandler.bind(this)}
        url="https://dinosaur-facts.firebaseio.com/dinosaurs"
      />
    );
  }
}

By default, FireAct listens to value events. You can use the evenType Prop to listens to other events ([ 'value', 'child_added', 'child_changed', 'child_removed', 'child_moved'])

Once [➤]

Listens for exactly one event of the specified event type, and then stops listening

<FireAct
    once
    onDataChange={this.dataChangeHandler.bind(this)}
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

OrderByChild [➤]

Generates a new Query object ordered by the specified child key.

<FireAct
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByChild="height"
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

OrderByKey [➤]

Generates a new Query object ordered by key.

<FireAct
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByKey
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

orderByValue [➤]

Generates a new Query object ordered by key.

<FireAct
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByValue
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

orderByPriority [➤]

Generates a new Query object ordered by key.

<FireAct
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByPriority
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

startAt [➤]

Creates a Query with the specified starting point. The generated Query includes children which match the specified starting point.

<FireAct
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByChild="height"
    startAt={3}
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

endAt [➤]

Creates a Query with the specified ending point. The generated Query includes children which match the specified ending point.

<FireAct
    endat="pterodactyl"
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByKey
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

equalTo [➤]

Creates a Query which includes children which match the specified value.

<FireAct
    equalTo={25}
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByChild="height"
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

limitToFirst [➤]

Generates a new Query object limited to the first certain number of children.

<FireAct
    limitToFirst={2}
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByChild="height"
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

limitToLast [➤]

Generates a new Query object limited to the last certain number of children.

<FireAct
    limitToLast={2}
    onDataChange={this.dataChangeHandler.bind(this)}
    orderByChild="weight"
    url="https://dinosaur-facts.firebaseio.com/dinosaurs"
  />

Writing Data

In order to write data use the bindTo prop. Any time the data passed to bindTo changes, it will be updated on firebase.


render(){
  let data = {
    user: 'Homer',
    message: 'I love Pork Chops'
  }
  return (
    <FireAct
        bindTo={data}
        url="https://fireact.firebaseio.com/someId"
      />
  );
}

If you want to push the data in a new child location with a unique key, add the push prop [➤]

render(){
  let data = {
    user: 'Homer',
    message: 'I love Pork Chops'
  }
  return (
    <FireAct
        bindTo={data}
        push
        url="https://fireact.firebaseio.com/dinosaurs"
      />
  );
}

'2 Way binding'

You can achieve two way binding by using both the bindTo and the onChangeData props :

class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {
        user: 'Homer',
        message: 'I love Pork Chops'
      }
    }
  }

  dataChangeHandler(data) {
    this.setState({
      data: data
    });
  }

  render() {
    return (
      <FireAct
        bindTo={this.state.data}
        onDataChange={this.dataChangeHandler.bind(this)}
        url="https://dinosaur-facts.firebaseio.com/dinosaurs"
      />
    );
  }
}
bricef commented 8 years ago

I like @fdidron's idea of using a react component for the FB values. Still some work to be done on more complex examples, but it seems like a reasonably direction.

bigblind commented 8 years ago

There are 2 things that somewhat bother me about @fdidron's proposal.

First of all, you're adding properties for every single option and operation for Firebase refs. Why not pass in the ref as a single property? This decouples the library more from Firebase, allowing it to keep working, even when Firebase expands its functionality.

But what I find weird as well is the way data flows. Usually, a component gets the data it needs from its parent. The only situation where components usually get data from a child using a callback, is when that child needs to be rendered by the component, and the component needs to respond to user input. What would the FireAct component render? If nothing at all, then, why is it in the render tree?

fdidron commented 8 years ago

@bigblind Concerning your first comment, I understand the tradeoff of coupling the Firebase lib to the component. However the idea is to provide a declarative JSX way of binding to Firebase, a bit like react router allows you to declare routes.

For the second comment, I agree with you, come to think of It, I could make the component a container (or Provider like redux) and pass down the binded value as props to its direct children. I could even pass the value further down nested components using react's context, and provide a function to map the bindings to props. What do you think ?

oscar-b commented 8 years ago

Saw this library, might be worth looking at as well: https://github.com/tiberiuc/redux-react-firebase

prescottprue commented 8 years ago

Great discussion.

Its nice having decorators or connecting to state management of some kind (like redux) so your smart components can more easily specify their data dependencies.

redux-react-firebase uses decorators which is nice, but it isn't updated to Firebase v3+ yet.

1j01 commented 8 years ago

I think A component is way overcomplicating the API. It should just be a single call to attach methods to the class or instance, or just static methods on the ReactFire object that take a this property:

class User extends React.Component {
  constructor (props) {
    fPosition = props.fPlayer.child("position")
    fContacts = props.fPlayer.child("contacts")
    ReactFire.bindAsObject(this, "position", fPosition)
    ReactFire.bindAsArray(this, "contacts", fContacts)
  }
  render () {
    {contacts, position} = state
    {x, y} = position
    <div class="user" style={left: x, top: y}>
      <Contacts contacts={contacts}/>
    </div>
  }
}

(Those methods could do some initialization the first time something is binded, and overload the componentWillUnmount, removing listeners and calling the original. Perhaps not the cleanest solution internally, but it would make for a nice succinct API.)

bigblind commented 8 years ago

@1j01 That requires any object that wants to use Firebase to be a class, so you can't use it with function components. I still think a container is the most flexible option. Anything where you still need to manage binding/unbinding requires keeping state, which I think you should avoid as much as possible.

1j01 commented 8 years ago

@bigblind Okay, well I didn't consider function components (I've never used them) Has ReactFire previously worked with function components? (And does it really need to?)

bigblind commented 8 years ago

I just spent 3 hours writing an elaborate answer to this question only then to accidentally open another page in that tab... 😭

Here's the gist of it:

I might update this post with more useful links when I'm less depressed about browsers not saving draft form responses... 3 hours... wasted.

1j01 commented 8 years ago

@bigblind What browser are you using? Chrome seems pretty good about saving things, navigating forwards, backwards, closing and reopening tabs. Not that I haven't lost plenty of stuff to Chrome.

Anyways, seems like a reasonable approach.

birdwell commented 8 years ago

Hey, what's the current status of this conversation?

jwngr commented 8 years ago

I actually finally found some time this past weekend to test out a new HOC API myself. I am hoping to have a proposal in PR form soon. I'm leaning towards going with an API similar to what @bigblind suggested, but there are still a few performance concerns that need to be figured out (mainly around re-binding Firebase listeners upon changes to props).

prescottprue commented 8 years ago

@jwngr That is awesome to hear! Excited to check it out.

birdwell commented 8 years ago

@jwngr Sweet, I will be eagerly waiting! @prescottprue love your v3 lib on firebase redux! That's what's keeping me a float currently.

prescottprue commented 8 years ago

Thanks @birdwell, that is exactly what it is for!

For those who haven't seen it: redux-firebasev3 was made as a way to get v3 implemented in React/Redux. I figure that ReactFire will take its place eventually, but I needed a solution before then.

oscar-b commented 8 years ago

Same with re-base, there's a PR awaiting merge which adds v3 support: https://github.com/tylermcginnis/re-base/pull/104

mkozhukharenko commented 8 years ago

@oscar-b re-base is already updated to v3

marcello3d commented 8 years ago

Here's a completely different approach I've been experimenting with: https://gist.github.com/marcello3d/0e9a1e532954f67c1fbb67c7d18319a6

I'm bypassing ReactFire altogether, and instead made two APIs:

  1. A higher-order component inspired by react-resolver but uses Observables instead of Promises
  2. A collection of functions that create Observables from Firebase database.References.

Example usage:

import React from 'react'

import { observeValue, observeCurrentUser } from './firebase-observable'
import connectObserver from './connect-observer'

export default connectObserver({
  // function takes props and returns an Observable or Promise
  post: ({ postId }) => observeValue(firebase.database().ref('/posts').child(postId)),
  user: () => observeCurrentUser(firebase.auth())
})(class Post extends React.Component
  static propTypes = {
    postId: React.PropTypes.string.isRequired,
    post: React.PropTypes.shape({ … }),
    user: React.PropTypes.object
  };
  render () {
    const { post, user } = this.props
    return …
  }
}))
ghost commented 8 years ago

Do the owners of the reactfire repo have a recommended approach, or stated plans for using reactfire w/out mixins?

jwngr commented 8 years ago

@hyperbrave - https://github.com/firebase/reactfire/issues/38#issuecomment-238967140 is still the latest. I really want to get this out but I haven't found the time to finish my proposal yet.

vimtaai commented 8 years ago

Hi! I've been working on a library of my own for a while, and finally I managed to get it to a stage that it's ready for sharing. (altough there still may be bugs) I'm starting to use it for a project of my own. The main idea is to implement the Flux architecture with Google Firebase as the store and using Rx for the unidirectional flow. Although it is completely independent of React it works quite well with it. It also features a local in-memory storage with similar one-way data binding to variables or callbacks. Check out the library at https://kozta.github.io/fluxbase Any ideas, comments, questions are welcome.