Composing Custom Subscriptions

Writing a Subscription

The Basics

The most basic subscription looks like this:

const myCoolSubscription = () => (dispatch) => {
// Do your stuff here
return () => {
// Clean your stuff up here
};
};

If this doesn't do everything you want, we can definitely add some arguments:

const myCoolSubscription = (a, b) => (dispatch) => {
// Do your stuff here with a and b
return () => {
// Clean your stuff up here
};
};

This is all well and good, but there is a hitch - your parameters need to be consistent. If you are dynamically creating an object, or pass in different values, your subscription will be reset. One thing about subscriptions is they introduce one-way communication, being that you set a few defaults to initialize the subscription, but all other communication is from the subscription back into the application.

Smart Subscription Parameters

One very important detail to remember with subscriptions is a change in parameters will do a clean up of the old subscription and rebuild a new one. This means a change in value or reference. This means changing a value from 0 to 1, 'hello' to 'goodbye', or passing an inline function like () => 'foo'. Knowing when it's a good time to change these values to rebuild subscriptions can be hard, and knowing how to keep the same reference is equally hard. Here's an example of a bad subscription:

const { app, effects } = require('ferp');
const tickEffect = () => ({ type: 'tick' });
const tickSubscription = delay => dispatch => {
console.log('Create tick sub every', delay, 'milliseconds');
const handle = setInterval(effects.thunk, delay, tickEffect);
return () => {
console.log('Destroy tick sub every', delay, 'milliseconds');
clearInterval(handle);
};
}
app({
init: [{}, effects.none()],
update: (_, state) => [state, effects.none()],
subscribe: state => [
[tickSubscription, (Math.random() * 1000) + 1000], // Hmmmm
],
});

This is bad because the result of (Math.random() * 1000) + 1000 will be different every time it is calculated (which will be every update), and as a result, will forcibly clean up the previous subscription and create a new subscription every application loop.

If you want a consistent calculated value, you should put it in state. To do this with the above example, it would look like:

const { app, effects } = require('ferp');
const tickEffect = () => ({ type: 'tick' });
const tickSubscription = delay => dispatch => {
console.log('Create tick sub every', delay, 'milliseconds');
const handle = setInterval(effects.thunk, delay, tickEffect);
return () => {
console.log('Destroy tick sub every', delay, 'milliseconds');
clearInterval(handle);
};
}
app({
init: [{ delay: (Math.random() * 1000) + 1000 }, effects.none()],
update: (_, state) => [state, effects.none()],
subscribe: state => [
[tickSubscription, state.delay], // That's better!
],
});