What do redux-saga API calls return? (take, for example)
Asked Answered
W

2

5

I'm very new to redux-saga and am reading through the documentation. One of the things I'm not understanding is what, specifically, the API calls return.

When I say "return", I suppose I'm asking two things

  • what is the return value of what the documentation calls the "factory function"? (that is, here https://redux-saga.js.org/docs/advanced/Testing.html the docs state that "Since Sagas always yield an Effect, and these effects have simple factory functions (e.g. put, take etc.)", what, in general, would be the return values of these factory functions?

  • what is the return value of these "factory functions" when yielded to the redux-saga middleware?

For instance, in the documentation (https://redux-saga.js.org/docs/advanced/Concurrency.html) they have

import {fork, take} from "redux-saga/effects"

const takeEvery = (pattern, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(pattern)
    yield fork(saga, ...args.concat(action))
  }
})

I understand, from looking at it, that here take when yielded returns the action that matches the pattern. Thus I take that to always be the return value. So that here (https://redux-saga.js.org/docs/advanced/NonBlockingCalls.html)

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    const token = yield call(authorize, user, password)
    if (token) {
      yield call(Api.storeItem, {token})
      yield take('LOGOUT')
      yield call(Api.clearItem, 'token')
    }
  }
}

user and password are fields in the returned action?

But where is this documented, that take, when yielded, returns the action? I went to the documentation for take, and while there's a fairly good description of what it does, I didn't see what it returns.

In general, is there some underlying assumption about the API calls and their return values that I'm missing, due to my newbie status? Or -- and this is a distinct possibility -- I may simply have overlooked where it mentions the return value.

Thanks for any insights -- and if you know of a good alternative, thorough overview of redux-saga, I'd welcome any links. I've been poring through tutorials and blog posts, but would love a good in-depth look.

Witting answered 23/11, 2018 at 17:32 Comment(0)
I
6

How redux-saga works

Ok, my take on how redux-saga works.

First of all, with redux-saga, you are writing using ES6 generator functions. A generator function works like a music box: as you turn the handle, it plays notes one by one. When you stop, it stops.

In our case, it's redux-saga runtime who turns the handle. The notes are the effects that your saga yields.

Generator functions that you write are completely passive. They yield plain objects called effects. You can print an effect to see how it looks like:

console.log(take('SOME_ACTION'))

You'll see it's just an object. The take() function just creates such an object, it has no side effects. If you forget to use yield, for example, nothing will happen.

How take() works

Let's suppose you yield a take() effect from your saga:

const action = yield take('SOME_ACTION')

When redux-saga runtime executes your saga, it does something like this:

// It initializes the generator
let gen = yourSaga()

// And then it turns the handle,
// extracting effects that you yield, one by one
while (true) {
  // ...
  const effect = gen.next().value
  // Now redux-saga analyses your effect, and executes what you asked for
  // ...
}

When it eventually stumbles on your take() effect, it stops and puts off the generator for later.

Later, when redux-saga runtime detects that SOME_ACTION was dispatched, it finds your generator and continues executing it:

gen.next(action).value

But this time redux-saga uses a generator feature that a music box doesn't have. Redux-saga runtime supplies the action that was detected via the next() function. What you pass to next() then becomes the value of yield ... expression inside your generator.

I hope this is helpful, because redux-saga docs frequently slip into talking about generators.

Intendancy answered 24/11, 2018 at 11:45 Comment(4)
That was more or less my thinking on the matter, although I was envisioning the music box more as controlled by the generator, but I think your version is correct. Still, I'm curious where the actual values that the middleware passes to the generator are detailed. For instance, you say that it passes the action to take --- gen.next(action).value --- and that's what I see happening. SImilarly, I believe that if you call a function, the middleware will pass the return value of that function back on the next yield. But is any/all of this documented anywhere?Witting
Internal effect formats are not documented, I guess, because they may change. As for yield take(), I don’t see anything about its value in the docs, either. Lame docs :)Intendancy
Indeed, it would seem so. I was thinking more about the order of control in the 'music-box': is it not that the saga itself is the first thing with control, which it then hands over to the middleware, not the other way round as you suggested above? I was looking through an interesting tutorial (decembersoft.com/posts/…) where the author has created an entire game logic using sagas. It seems that sagas run the thing, handing over control to the middleware when necessary....Witting
@Witting I think sagas have two aspects to them: abstraction and implementation. On abstract level, a saga is an async function. The syntax purposefully mimics convenient ES6 async/await and makes you think you are in control. On the implementation level, contrarily, sagas are generators run by redux-saga runtime, so there is inversion of control. The docs don't properly explain this and often switch between the abstraction and the implementation.Intendancy
G
2

just looked into sagas for the first time.

as I understand it, take is similar to a listener. in your example a user is logged in and then taking the first LOGOUT that will happen.

in this case waiting for logout means, that you are waiting until the action LOGOUT is dispatched. So you will get the parameters that were dispatched with the logout.

the logout is probably dispatched as follows

put({type: 'LOGOUT', ...params})

params is what you get when you are listening to LOGOUT using take.

Gascon answered 23/11, 2018 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.