DATABASE EVERYWHERE
A Reactive Data Architecture
for JavaScript Frontends
Twitter @youyuxi / GitHub @yyx990803
What makes it hard to build a modern web app?
Keeping state in sync.
UI State
Client Data State
Server Data State
Everything can go wrong when you manage it imperatively.
UI State
Client Data State
We've gotten pretty good at dealing with this...
Client Data State
Server Data State
But how about this?
REST
Model Abstractions over REST
Flux Stores ?
Problems with the
client-model-over-REST abstraction
Trouble with non-conventional APIs
Difficulty managing optimistic updates
Pull-centric design leads to imperative code
What can we do?
Let the UI talk to the database!
UI State
Server Data State
Cutting-edge Developments in Frontend Data-fetching
Relay / GraphQL
Falcor / JSON Graph
Om Next
Unfortunately, none of these are ready yet...
METEOR
Tracker
Fullstack Reactivity
var light = new ReactiveVar('green');
Tracker.autorun(function() {
console.log('The light is', light.get());
});
light.set('amber');
light.set('red');
// Result:
> 'The light is green'
> 'The light is amber'
> 'The light is red'
Minimongo
Isomorphic Database Driver
UI State
Minimongo
Client Data State & Server Data State
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
Template.body.helpers({
// reactive helper function
// just return what you need
tasks: function () {
return Tasks.find({});
}
});
}
<ul>
{{#each tasks}}
<li>{{text}}</li>
{{/each}}
</ul>
// define a data updating method on server
Meteor.methods({
addTask: function (text) {
// Make sure the user is logged in before inserting a task
if (! Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Tasks.insert({
text: text,
createdAt: new Date(),
owner: Meteor.userId(),
username: Meteor.user().username
});
}
});
// calling it from client, as easy as:
Meteor.call("addTask", text);
Optimistic UI updates
Publish & Subscribe
Component-level Subscription
Template.notifications.onCreated(function () {
// Use this.subscribe inside onCreated callback
this.subscribe("notifications")
})
<template name="notifications">
{{#if Template.subscriptionsReady}}
<!-- This is displayed when all data is ready. -->
{{#each notifications}}
{{> notification}}
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
Did I mention realtime?
This is nice,
but what if I want to use another frontend framework?
meteor add urigo:angular
meteor add react
angular.module("simple-todos", ['angular-meteor']);
angular.module("simple-todos")
.controller("TodosListCtrl", ['$scope',
function($scope){
$scope.tasks = [...];
}
]);
<ul ng-repeat="task in tasks">
<li>{{task.text}}</li>
</ul>
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
angular.module("simple-todos", ['angular-meteor']);
angular.module("simple-todos")
.controller("TodosListCtrl", ['$scope', '$meteor',
function($scope, $meteor){
$scope.tasks = $meteor.collection(Tasks);
}
]);
}
<ul ng-repeat="task in tasks">
<li>{{task.text}}</li>
</ul>
+
var App = React.createClass({
getInitialState() {
return {
tasks: [...]
};
},
render() {
return (
<ul>
{this.state.tasks.map(function (task) {
return <li key={task._id}>{task.content}</li>;
})}
</ul>
);
}
});
var Tasks = new Mongo.Collection("tasks");
var App = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
// This method knows how to listen to Meteor's
// reactive data sources, such as collection queries
return {
// Return an array with all items in the collection
tasks: Tasks.find().fetch()
};
},
render() {
return (
<ul>
{this.data.tasks.map(function (task) {
return <li key={task._id}>{task.content}</li>;
})}
</ul>
);
}
});
+
Thanks!
Database Everywhere @ ShenJS
By Evan You
Database Everywhere @ ShenJS
- 15,763