effector / patronum

☄️ Effector operators library delivering modularity and convenience ✨
https://patronum.effector.dev
MIT License
297 stars 43 forks source link

Allow to skip spread updates with special token #328

Open zerobias opened 6 months ago

zerobias commented 6 months ago

One of key features of spread is an ability to skip updates for some targets, but it requires object mutation or weird conditionals (it becomes worse when amount of targets grows):

sample({
  clock: trigger,
  fn: upd => upd.both
    ? ({foo: 0, bar: 0})
    : ({foo: 0})
  target: spread({
    foo,
    bar,
  })
})

// or

sample({
  clock: trigger,
  fn(upd) {
    const result = {foo: 0}
    if (upd.both) {
      result.bar = 0
    }
    return result
  },
  target: spread({
    foo,
    bar,
  })
})

We can add special token SKIP which will mean that this unit will not be triggered:

import {spread, SKIP} from 'patronum'

// or maybe spread.SKIP

sample({
  clock: trigger,
  fn: upd =>({
    foo: 0,
    bar: upd.both ? 0 : SKIP
  }),
  target: spread({
    foo,
    bar,
  })
})
zerobias commented 6 months ago

After internal discussion we came to the conclusion that this would be an inconsistent solution - it misleads people into thinking that it is fn that does the filtering when it is spread that does it, it introduces new complex concepts to solve a very small problem.

This problem is easier to solve with native methods, although it may be a slightly more verbose solution

sample({
  clock: trigger,
  fn: (upd) => ({
    foo: 0,
    ...(upd.both ? {bar: 0} : null),
    ...(upd.baz ? {baz: 0} : null),
  }),
  target: spread({
    foo,
    bar,
    baz,
  })
})
zerobias commented 6 months ago

But the underlying issue remains opened:

1) A trigger from a single data source is required 2) For multiple units at once 3) With support for data transformation 4) With the ability to skip updates of any of them by arbitrary criteria

We'll continue to think about this idea, maybe go back to the option suggested above, maybe solve it some other way

AlexandrHoroshih commented 6 months ago

Actually, it looks like someting that prepend operator (proposed quite a while ago) should handle

sample({
 clock: trigger,
 target: spread({
    first: $first,
    second: prepend({
      filter: $allowed,
      fn: mapData
      target: $second
    }),
  }),
});

It does check all boxes: