undux icon indicating copy to clipboard operation
undux copied to clipboard

Safe plugin

Open bcherny opened this issue 7 years ago • 2 comments

This PR introduces the SafePlugin type and createSafeStore utility. They opt the user into a more restricted subset of Undux that is more aggressive about preventing cycles in Effects. The way it does this is by statically restricting calls to store.set to React components only (this means you can't call store.set from Effects).

This makes Undux less powerful by imposing a constraint on data flow similar to that imposed by Redux and Flux. It's analogous to a Redux/Flux constraint that prevents calling dispatcher.dispatch outside of a React component, or from within a dispatch call. This is not a safety mode that's useful for most users; cycles in Rx streams are rare, and may be better prevented by other means. This PR is a first pass to sketch out what the experience might feel like.

Future directions to explore:

  • Be more permissive: only ban calling .store.set from Effects, and allow everywhere else
  • Be more permissive: use a linter to prevent cycles in Effects, but allow store.set calls in general
  • Be more permissive: perform dynamic analysis to warn about cycles in Effects, but allow store.set calls in general https://github.com/bcherny/undux/pull/37
  • Also enforce this constraint with a runtime check

Usage

-const {createStore} = require('undux');
+const {createSafeStore} = require('undux');

-import type {Plugin} from 'undux';
+import type {SafePlugin} from 'undux';

bcherny avatar May 27 '18 03:05 bcherny

I'm curious how you'd wire up a request without set in effects. Normally I'd do something like:

  store.on('params').subscribe((p) => {
    store.set('response')(null);
    fetch(makeUrl(p)).then((response) => {
      store.set('response')(response);
    });
  });

hurrymaplelad avatar May 31 '18 17:05 hurrymaplelad

@hurrymaplelad With this proposed architecture, a lot of things that normally live in effects would live in action creators (aka. utility functions) instead. This makes Undux look a lot like Flux, but typesafe. I'm not convinced this is a good direction to go in; effects give order and meaning to action creators via RxJS's stream algebra, making it possible to sequence, compose, statically analyze, and otherwise combine actions. If we move actions back to action creators, we get rid of this set of features.

So your code might become something like:

Effects.js

// Empty

Actions.js

function requestData(store: Store) {
  return () =>
    fetch(makeUrl(store.get('params')))
      .then(store.set('response'))
}

Component.react.js

<button onClick={requestData(this.props.store)}>Send request</button>

bcherny avatar May 31 '18 17:05 bcherny