Sometimes multiple actions need to be chained to make a thing happen. For example: logging a user out means that:
redux has a concept of redux-saga to handle this. It's basically a way of combining multiple actions into a higher-order flow. Pretty damn interesting, and definitely useful. Actually I built exactly this stuff last year in the form of barracks.
So umm, I think probably the right way of dealing with this is to allow for callbacks from within effects so they can be composed into higher order effects (which we can then refer to as sagas, although they'd simply be a pattern).
An example:
const http = require('choo/http')
app.model({
effects: {
binbaz: (state, action, send) => {
http.get('/foo/bar', function (err, res, body) {
if (err) return send('oh no!', err)
send()
})
},
foobar: (state, action, send) => {
send('binbaz', function () {
console.log('hello world!')
send()
})
}
}
})
I feel like send() loses a bit of its semantic meaning here, perhaps an extra, optional cb() argument should be added? Anyway, I hope the idea comes across. I feel like the idea is there, but I'm overlooking quirks and the consumer-facing API isn't quite where it could be - suggestions are heaps welcome! - Cheers :sparkles:
another example is how to implement sagas in tom, which is the same pattern for inu.
from my understanding, for effects to work as sagas, you must be able to subscribe to all future actions and dispatch any number of actions at any time.
dammit, tom and inu are nailing it - gonna re-read their docs a couple of times and figure out what they're doing. Feel they're miles ahead hah
I've been giving this a lot of thought and I think that what's needed is a callback in effects to signify it's done executing and handle control back to the function that initiated it:
const http = require('choo/http')
app.model({
effects: {
makeXhrRequest: (state, action, send, cb) => {
http.get('/foo/bar', function (err, res, body) {
if (err) return cb(err)
cb()
})
},
foobar: (state, action, send, cb) => {
send('makeXhrRequest', function (err, res) {
if (err) {
console.log('all is bad D:')
cb()
} else {
send('makeXhrRequest', function (err, res) {
if (err) {
console.log('all is bad D:')
cb()
} else {
console.log('all is good :D')
cb()
}
})
}
})
}
}
})
Obv in a real world scenario we'd use proper async abstractions such as map-limit or pull-stream, but I hope the point comes across. Does this make sense?
Should prob make the callback optional by doing a Function.prototype.length check for the params + not pass the same send() function into the views as the models to prevent accidental leaking of callback values into the views (which would break unidirectionality)
A very similar pattern has arisen from my redux action code.
Only difference is %s/send/dispatch/g %s/cb/next/g
Careful with calling them sagas though since people have a very specific coupling of syntax i.e. generators etc. with that name.
ah yeah you're right, at this point I reckon calling it "composable effects" or "higher order effects" is probabably better - nice one!
If we're going to build choo / yo-yo guide we should probably put a migration guide for react in there hah
:+1: on having a migration guide.
You could just have these be covered in the async section of the user guide.
tbh the fact that you needed to add a middleware to support async with redux was a code smell.
I also _really_ like that this approach does not ram promises down my throat. I would be very happy to see that be an add-on if ever introduced.
@yoshuawuyts Could you write up an example of using pull-stream to manage that callback hell?
@bitwiselove I'd love to, but I'm slightly swamped in getting v3 out first (choo's suddenly gotten popular haha) - perhaps take a look at inu, https://pull-stream.github.io/ or ask @ahdinosaur nicely (':
I'm sure we'll get more of this out there post v3 - just trying to get through this initial spike of popularity and breaking changes unscathed rn haha
hey @bitwiselove, do you have a specific real-world use case in mind? feel free to make an issue on inu or pull-stream with any questions, happy to help. :cat:
Closing as 3.0 is imminent; wanna make sure all issues are taken care of. Releasing soooonâ„¢ :sparkles:
Most helpful comment
:+1: on having a migration guide.
You could just have these be covered in the
asyncsection of the user guide.tbh the fact that you needed to add a middleware to support async with redux was a code smell.
I also _really_ like that this approach does not ram promises down my throat. I would be very happy to see that be an add-on if ever introduced.