npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-ddp

v1.1.11

Published

ddp client with subs, methods, minimongo supported without much work (see readme), will not make minimongo a dep.

Downloads

13

Readme

react-ddp

A lightweight ddp-client for react-native. Expanded on work by mondora.

Purpose

The goal is not to implement a full-blown Meteor client in react-native. This library is meant to include minimally necessary set of functions which are implemented on traditional Meteor clients, but to say absolutely nothing about how your data is handled. You may use redux, minimongo, or a custom object to store data; whatever you want. I tried to couple this library with my own store until I realized two things:

  1. Creating a reactive front-end data store is non-trivial.
  2. React-native users have mixed opinions about data storage, best to not force them.

Thus, this ddp client does not implement a cache of any sort. You will get the traditional added, changed, and removed messages (compliant with the DDP version 1 spec) which you will use to populate a (potentially reactive) data-store yourself.

With react-ddp and minimongo-cache, we can achieve a front-end on react-native which is eerily similar to a traditional Meteor front end, with little work on your end, whilst being modular. Don't like minimongo? Feel free to use Redux or something. The coupling between the data-store and react-ddp is as easy as writing custom added, changed, and remove callbacks. For minimongo, this is trivial because the ddp messages are already in "mongo" form. An example below will show you how easy it is to get started with Pete Hunt's minimongo-cache and react-ddp.

Implementation

Reactive Client Metadata

ReactiveVar (npm install reactive-var) is used sparingly in react-ddp to provide a reactive handle to user state:

Client.loggingIn() //true or false
Client.userId() // "someUserId" or null
Client.status() // "connecting", "connected", "reconnecting", "disconnected"

While Tracker (npm install trackr) is not an explicit dependency of react-ddp, you will need Tracker in your application in order to make use of the reactive nature of ReactiveVar. Read more about how to use Tracker with react-native

Main methods

Instantiation

In the top level of your react-native app, create an instance of the react-ddp client:

//connect.js

import DDP from "react-ddp";
export const client = new DDP({
  SocketConstructor: WebSocket,
  endpoint:"ws://localhost:3000/websocket",
  //endpoint: "wss://mysecurewebsite.example.com/websocket",
  debug:false,
  autoConnect: true,
  autoReconnect: true,
  appId: "myAwesomeAppSDFSLjfwqijdkf",
  reconnectInterval: 10000
})

Read more about debug and appId here See mondora's ddp.js docs for more information on things like endpoint, autoReconnect, etc.

Regarding Encryption, Authentication & Passwords

As of now, when you call a method via client.call(...), any key in the data field of opts which is one of: "password", "oldPassword", "newPassword" will be encrypted before it is sent to the server. This is used in the login method, as well as custom sign up/ password changing methods.

When the react-ddp client is instantiated, the socket opens and sends a connect message to server automatically (thanks to mondora's raw ddp client). react-ddp will attempt to (re)log the user in automatically once a connection has been successfully (re)established. This is achieved by using react-native's AsyncStorage. Read about authentication state flicker and how it's fixed with react-ddp

Methods

import { myValidationFunction } from "./validation.js";
import { client } from "./connect.js";

client.login

...
client.login({
  check: myValidationFunction, //takes args (data, currentUserId)
  data: {
    user: {
      email: "[email protected]"
    },
    password: "terriblePassword" //not sent in plain text over the wire (encrypted)
  }
}, optionalCallback)

//optionalCallback is provided (error, result) arguments.

client.logout

...
client.logout(optionalCallback)
//optionalCallback is provided (error) arguments.

client.subscribe

The syntax differs slightly from vanilla Meteor. Supports caching.

...
client.subscribe({
  cache: false, //default
  check: myValidationFunction, //takes args (data, currentUserId)
  data: {
    location: [42.2345,-72.68583],
    topic: "Trees"
  },
  name: "treesAroundMe"
}, optionalCallback)

//optionalCallback is provided with (error, sub) arguments.
//sub is of the form {id: "someSubId"}

Subscribes to the treesAroundMe subscription with a single argument (data). If cache is true, then this subscription will not get overridden by a new subscription of the same name. For example (with cache === false) if we perform the same subscription with data.locationequal to[40, -72], then the client will unsub from the previous subscription, and will subscribe to the new one. By default, a call to the exact same subscription (same name, same data`) will not result in a re-run of the subscription.

client.unsubscribe

...
client.unsubscribe({
  id: "theSubIdIfKnown", //if you know this, you don't need to enter name or data.
  name: "nameOfSub", //if you don't have the id, but know the name.
  data: {
    some:"data"
  }
}, optionalCallback)

//optionalCallback is provided with (error, sub) arguments.
//sub is of the form {id: "someSubId"}

If you provide the id, the client will try to unsub from that exact id. If you provide a sub name, you should provide the data passed to the sub as well so that you can differentiate between possibly cached subscriptions.

client.call

The syntax differs slightly from vanilla Meteor.

client.call({
  name:"someMethod",
  data: {
    userInput1: "I love potatoes",
    userInput2: "I love tomatoes"
  },
  check: myValiationFunction //takes args (data, currentUserId)
}, optionalCallback)

//optionalCallback is provided (error, result) arguments.

Using minimongo-cache

Using this library with minimongo-cache is relatively easy because of how DDP messages are structured. On the client:

//database.js
import minimongo from "minimongo-cache";
import { client } from "./connect.js";

process.nextTick = setImmediate; //react-native polyfill
const db = new minimongo();

//initialize collections
const collections = ['posts','users','messages','trees','animals'];
collections.forEach(function(coll){
  db.addCollection(coll)
})

client.on('added', function(data){
  if (!db[data.collection]){
    db.addCollection(data.collection);
  }
  db[data.collection].upsert({_id: data.id, ...data.fields})
})

client.on('changed', function(data){
  db[data.collection].upsert({_id: data.id, ...data.fields});
})

client.on('removed', function(data){
  db[data.collection].remove({_id:data.id});
})

export { db }; //use db.posts.find(...), db.posts.findOne(...), etc. as in minimongo-cache

minimongo-cache makes it easy to get reactivity into react-native:

...
import { db } from "./database.js";
export default class MyComponent extends Component {
  constructor(props){
    super(props);
    this.state = {posts:[]}
  }
  componentWillMount(){
    this.observer = db.observe(()=>{
      return db.posts.find({},{fields:{name:1,body:1}})
    })
    this.observer.subscribe((posts)=>{
      //this function will run whenever the result set "posts" changes
      //for example, due to your incoming DDP messages!
      this.setState({posts:posts})
    })
  }
  componentWillUnmount(){
    this.observer.dispose();
  }
  render(){
    ...
  }
}