Take every requires a saga parameter - redux

I am getting this error when i am fetching data from api
export function* fetchAllProductsAsync()
{ const response = yield call(loadAllProductsApi)
yield put({type:types.ALL_PRODUCTS_SUCCESS, payload:response.data})
}
export function* productSaga()
{
yield takeEvery({type:types.ALL_PRODUCTS_REQ, fetchAllProductsAsync})
}
export default productSaga
function* rootSaga(){ yield all([productSaga])}
export default rootSaga

You are mixing together takeEvery and put interface.
The put effect expects just one parameter - the action object with type payload etc. However, the interface of takeEvery is different, it expects multiple parameters, where the first one is usually the type of the action as a string and the second one is the saga that gets called.
So your takeEvery call should look like this:
yield takeEvery(types.ALL_PRODUCTS_REQ, fetchAllProductsAsync)
For extra details, check out the docs:
https://redux-saga.js.org/docs/api/#takeeverypattern-saga-args

Related

Wrap all saga actions in a common generator function

I am using redux-sage in my application and below is my code
export default function* () {
yield takeLatest(ActionTypes.VALIDATE_INPUT, checkForInputValidity);
yield takeLatest(ActionTypes.ON_REFRESH, onRefresh);
yield takeLatest(ActionTypes.ON_SUBMIT, onSubmit);
}
Is there a way to make sure I call a common generator function before any action is handled. For example,
whenever I dispatch some action, I want to update a variable in redux-state. this is common across all actions. What I am trying to avoid here is to duplicate some piece of common code in every action handler
You can use simple reducer function which skips action type check and track all the actions, and i.e. return the current timestamp:
export default () => + new Date()
You can call take with no arguments or with '*' in order to match all actions, per the docs.
The part that might get tricky (though still totally doable!) is getting the correct order of generator functions. Is it important the universal generator is run before the other generators, or is it ok if they are all run at once asynchronously? The docs on concurrency, fork vs spawn, and running tasks in parallel might illustrate the difference.
Untested, but I think this is what you want:
import {all, takeLatest, takeEvery } from "redux-saga/effects";
function* universal(action) {
// do something
// if dispatching anything here, make sure that the dispatched action doesn't get picked up again and create an infinite loop
}
export default function* () {
// univeral generator is called with a blocking yield
yield takeEvery( '*', universal);
// run individual generators in parallel to each other
yield all ([
takeLatest(ActionTypes.VALIDATE_INPUT, checkForInputValidity),
takeLatest(ActionTypes.ON_REFRESH, onRefresh),
takeLatest(ActionTypes.ON_SUBMIT, onSubmit),
]);
}
Alternatively, you could write some sort of custom middleware.

Redux toolkit actionCreator warn: Invalid number of arguments, expected 0

it's action:
export const someAction = createAction('SOME_ACTION')
Is there a way to fix it?
Invalid number of arguments, expected 0
dispatch(someAction({key: 'val'}))
Okay, I found how, you just need to add function prepare by second argument like this
export const someAction = createAction('SOME_ACTION', prepare)
well, after this the next warn in saga
Unresolved variable type someAction.type
function* watch() {
yield takeLatest(someAction.type, getOneClientWork);
}
OMG!
As for your second problem: Try
function* watch() {
yield takeLatest(someAction, getOneClientWork);
}
But in general, those are TypeScript warnings that come from your IDE applying TypeScript to pure, untyped JavaScript code - in the hopes of giving you better autocompletion and hints. Unfortunatley, stuff like that goes wrong a lot of times.
So if you were writing TypeScript, you'd write
export const someAction = createAction<PayloadType>('SOME_ACTION')
and if you don't do that, it goes back to the default behaviour:
export const someAction = createAction('SOME_ACTION')
// defaults to
export const someAction = createAction<void>('SOME_ACTION')
and would mean "this takes no payload".
Now your IDE blindly applies TypeScript there, uses the default void and you end up with warnings that don't really concern you.

Redux saga: take every action where error is true

Is there a possibility to specify whether the action has its error field set to true?
const response = function*() {
yield takeEvery("CLIENT_RESPONSE", handleResponse);
}
However, we don't know whether the action with type CLIENT_RESPONSE has its error field set to true or not.
I know I can check this in the handleResponse but that seems to be more work than it should. For instance, the handleResponse might get complex because for both the non-error and error case I need to write a lot of code (i.e. I want to have different handlers for both cases).
So is there a way to specify to only take that action when error is set to true?
According to Saga API reference, the pattern (first argument) of takeEvery can be String, Array or Function.
You can achieve what you want by passing a function:
const response = function*() {
yield takeEvery(action => (action.type === "CLIENT_RESPONSE" && !action.error), handleResponse);
}

Calling an external function with redux-saga yield call appears to give context errors

I have a redux-saga watcher/saga setup as below:
function* parseSaga(action) {
let result = corewar.parser.parse(action.redcode)
yield put({ type: PARSE, result })
}
export function* parseWatcher() {
yield takeLatest(PARSE_REQUESTED, parseSaga);
}
Where corewar is an imported npm package I've written.
When the function is written as above, it works as expected, but I'd like to wrap the call to parse in a yield call so that I can better test things as described in the docs here: https://redux-saga.js.org/docs/basics/DispatchingActions.html
However, when I wrap up the function call like so:
let result = yield call(corewar.parser.parse, action.redcode)
I get an error which appears to come from my npm package as follows:
uncaught at parseWatcher at parseWatcher
at takeLatest
at parseSaga
TypeError: Cannot read property 'scanner' of null
at Parser.parse (http://localhost:3000/static/js/bundle.js:2017:28)
at runCallEffect (http://localhost:3000/static/js/bundle.js:43679:19)
at runEffect (http://localhost:3000/static/js/bundle.js:43601:648)
... and so on
Scanner in this case is an internal property to the Parser class which is called in the parse method as shown below (in typescript):
public parse(document: string, options?: IParseOptions): IParseResult {
options = Object.assign({}, Parser.DefaultOptions, options || {});
var context = this.scanner.scan(document, options);
... other stuff
}
So it appears like somehow through using this saga it's got inside my npm package and messed up the this reference?
It seems like I need to somehow ensure the previous context is retained but I wasn't sure how to achieve this as I'm not aware of how it's become lost by just wrapping the external call up in the redux-saga call function.
EDIT: The plot thickens
So it's definitely a context issue, but it seems related to calling nested function calls. I've tweaked the npm package so that parse is also exposed from the root object and now see the following results:
Works
let result = yield call(corewar.parse.bind(corewar), action.redcode)
let result = yield call([corewar, corewar.parse], action.redcode)
but the original nested method does not
Does not work
let result = yield call(corewar.parser.parse.bind(corewar), action.redcode)
let result = yield call([corewar, corewar.parser.parse], action.redcode)
I'm willing to expose the public interface from the root object (as it was on my todo list anyway) but is this the expected result? or some quirk?
Isn't this working
let result = yield call(corewar.parser.parse.bind(corewar.parser), action.redcode)
Since parse is a method of coreware.parser and not just coreware.
If this is not working then why not:
const parse = code => corewar.parser.parse(code);
let result = yield call(parse, action.redcode)

Redux Saga - Take Every is never Called

I have this simple saga:
export function* priceComparisonSaga() {
yield takeEvery(RECORD_PRICE, priceComparison);
}
But whenever I dispatch the event, it is never called. What's going on?
It turns out the action in question had an extra "type" property.
When using the shortcut to create actions like this,
export const addBalance = makeActionCreator(ADD_BALANCE,"balance","type");
This action won't work, because the second type property overwrites "ADD_BALANCE". Instead, this works:
export const addBalance = makeActionCreator(ADD_BALANCE,"balance","balancetype");

Resources