raj icon indicating copy to clipboard operation
raj copied to clipboard

Redundant view updates?

Open mindplay-dk opened this issue 3 years ago • 3 comments

If something has an effect, and the effect dispatches another update, the view effectively ends up getting updated twice.

If that effect has an effect, three updates, and so on.

Is this by design?

mindplay-dk avatar May 20 '21 05:05 mindplay-dk

@mindplay-dk Generally an effect is asynchronous, so when you dispatch a message it won't cause an immediate update again. If you find that you're modeling something synchronous as an effect, I think that's a good time to step back and consider if it ought not be a pure helper function called from your update(msg, model) instead.

Hope this helps! Given how simple Raj is, it definitely could be abused to thrash renders and updates. Like anything it'll need some consideration if you start running into performance bottlenecks but I've not had a problem here in particular.

andrejewski avatar May 20 '21 22:05 andrejewski

I'm just getting reacquainted, and I think where I got confused again is by the return tuples with the effect in there - it makes it look like there might be something asynchronous going on in the library itself, which I know there's not, but... whenever you have to return a function, it's natural to wonder, so when does it get executed? In this case, it's synchronous - it gets executed immediately, it's just not obvious from the calling code.

I have this other little thing I've been playing with:

https://gist.github.com/mindplay-dk/135124139217f08bf2903c2553cf1aac

It's different from Raj, using an object as state, allowing partial updates - which works better for my use-case.

But if you ignore that...

Instead of returning effects, I went with simply passing the run function, both to actions and to the view - so you can queue up as many action "effects" as you want. Similarly to Raj (I think?) this guarantees that one action will finish before another runs - if an action queues up effects with run, these aren't run immediately, they run after the action that queued them has returned; but you don't have to wait until the end of the action to queue up effects, you can put them wherever you want.

It has similar behavior, but doesn't require tuples (which still to this day confuse me) and doesn't limit you to one effect.

I don't know if that's really a meaningful difference - I think I just like the idea of functions returning one thing, and if I can pass a run function that achieves the same thing (even if it's called more than once) that just seems to produce slightly more readable code to me? After all, when you return an effects, you're not really returning a value or something you created - it's really just a mechanic to make sure things execute in full, and in order, right? My run function seems to do that, but it just helps my understanding, seeing the word "run" in the function.

It's probably mostly a matter of preference, I guess. Anyhow, feel free to carry on the discussion if you like, or just close the issue, since there's no issue with your library here. 🙂

(and I still like it, I'm just concerned about future maintainers being able to read and understand the code - it's still quite difficult for me, and I've spent quite a bit of time with it, which is why I'm looking for ways to make things more readable without adding substantial complexity...)

mindplay-dk avatar May 22 '21 09:05 mindplay-dk

@mindplay-dk I'll respond to only a few of these bits for time's sake, but I'd be wary of your action queuing pattern. It's certainly an option I've seen in other frameworks (Ember has a "run loop" everyone hates, for example) that can grow to be quite complicated to manage.


Similarly to Raj (I think?) this guarantees that one action will finish before another runs

Raj guarantees the effects which are processed by the runtime will be kicked off synchronously (which gives us this order), but effects can finish asynchronously and are not awaited.


I think I just like the idea of functions returning one thing, and if I can pass a run function that achieves the same thing (even if it's called more than once) that just seems to produce slightly more readable code to me?

Definitely a personal preference. I find code where every meaningful operation needs to be wrapped in a function call to be very noisy. What I want is plain, testable logic without needing to provide a fake run method to exercise everything. A framework shouldn't dominate the syntax for your business logic, it always ends up bad when you go to test/refactor it.


and doesn't limit you to one effect.

You're definitely not limited to one effect! Check out raj-compose/batch-effects for example. Effects are just functions which take a dispatch, that function can call other effects/functions with that dispatch.


I don't know if that's really a meaningful difference

Returning effect values makes it easier to test which is the focus of Raj. Take this snippet from your gist as an example:

setTimeout(() => {
  run(state => {
    console.log("ACCEPT MARKETING", state);
    return { acceptMarketing: true };
  });
}, 1000);

This is really hard to test, we've now got asynchrony in our update function which means we'd have to do some weird mocking tricks. Instead, Raj forces you to return an effect that can be called independently at Raj's (or when under test, your own) choosing. But you don't have to call it to get access to the next state.

andrejewski avatar May 23 '21 15:05 andrejewski