question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Pattern to handle reactive data changes in Meteor using sagas, perhaps using runSaga()

See original GitHub issue

I’m trying to come up with a pattern for handling changes to reactive data. Without using sagas, I might be tempted to write something like:

Meteor.autorun(() => {
  const user = Meteor.user();
  dispatch({
    type: "USER_CHANGED", 
    payload: user
  });
});

This will fire the action anytime the user changes. I can’t help but think that there is a much better way to do this, possibly using runSaga() but I really need some help seeing the way forward. The problem is essentially: How do I integrate reactive changes into the redux universe? Ideally, the pattern would support starting and stopping the monitoring of the reactive data. Being able to discern what exactly changed in the data (USER_LOGOUT, USER_DELETED, etc) would be grand, but mostly what I’m wanting is to have all the code that does this kind of thing built around the same infrastructure as the rest of the side-effect generating code. In other words, built on sagas and generators. Thanks for any help.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:8

github_iconTop GitHub Comments

3reactions
jschliebercommented, Jun 3, 2016

Here is my personal approach to sync Meteor Collections with the Redux store. The meteorDataSaga generator function* takes an Array of Mongo.Collection as its first parameter:

import {Tracker} from 'meteor/tracker';
import {eventChannel} from 'redux-saga';
import {
  take,
  call,
  put,
} from 'redux-saga/effects';

export const METEOR_FETCH_DATA = 'METEOR_FETCH_DATA';

function fetchData(Collection) {
  const entityName = Collection._name;
  return eventChannel(dispatch => {
    const computation = Tracker.autorun(() => {
      const data = Collection.find().fetch();
      setTimeout(() => dispatch({data, entityName}), 0);
    });
    return () => {
      computation.stop();
    }
  });
}

function* observe(channel) {
  while (true) {
    const event = yield take(channel);
    yield put({type: METEOR_FETCH_DATA, ...event});
  }
}

export default function* meteorDataSaga(Collections) {
  const channels = [];
  for (let Collection of Collections) {
    channels.push(yield call(fetchData, Collection));
  }
  const observers = channels.map(channel => observe(channel));
  yield observers;
}

The meteorDataSaga dispatches an action of type METEOR_FETCH_DATA with data and entityType properties whenever the reactive computation for a Collection reruns. It’s then up to the reducer to handle these actions. I normalize the data into an Immutable data structure with an entities reducer:

import Immutable from 'immutable';
import {
  normalize,
  Schema,
  arrayOf,
} from 'normalizr';
import {METEOR_FETCH_DATA} from '../sagas/meteor_data';

function normalizeEntities(data, entityName) {
  const schema = new Schema(entityName, {idAttribute: '_id'});
  return Immutable.fromJS(normalize(data, arrayOf(schema)).entities[entityName]);
}

export default function entities(state = Immutable.Map(), action) {
  if (action.type === METEOR_FETCH_DATA) {
    return state.set(action.entityName, normalizeEntities(action.data, action.entityName));
  }
  return state;
}

This way my Redux store is always in sync with all the local MiniMongo Collections i provide to the Saga. Hope this helps and please let me know if there is a better and more elegant way to do this…

0reactions
jschliebercommented, Jan 4, 2017

That pretty much depends on your requirements. The simple fetch works well as long as you don’t have to deal with really large data sets. But also of course if you want a more fine grained tracking of data changes then observe or even observeChanges is the preferred path…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Reactive Programming (RxJS 5 Observable) and Redux ...
This “gist” will give you a guide using RxJS 5 (Observable), ngrx/store (Redux Store) and ngrx/effects (Redux Saga) in an Angular2-Meteor ...
Read more >
Redux saga testing using runSaga not updating the state
This saga gives to call to an API and stores its response in the state by calling an action. After storing it, I...
Read more >
3 Real time Data With Meteor - Full Stack Reactive ... - YouTube
New episodes every week!As always checkout Level Up Tutorials here: https://www.leveluptutorials.com/and listen to myself and Wes Bos on ...
Read more >
Reactive Programming, State Management, and Redux for the ...
JavaScript can be a scary place where we create and change variables at will and while code can be written very quickly, ...
Read more >
Meteor Deep Dive: Reactive Programming With Tracker
Tracker is a Meteor's core package, it is a small but incredibly powerful library for transparent reactive programming in Meteor. Using ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found