Wrap all saga actions in a common generator function - redux

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.

Related

Schedule a cron job with schedule expression from onRequest cloud function parameter

Keeping a cron job pub/sub function (functions.pubsub.schedule), within a cloud function (functions.https.OnRequest) and exporting it, does not execute.
A complete example is as follows:
export const sayHelloWhen = functions.https.onRequest((request, response) => {
cors(request, response, () => {
const scheduleExpression = request.body.data.scheduleExpression;
functions.logger.log(`Called sayHelloWhen with ${scheduleExpression}`);
functions.pubsub.schedule(scheduleExpression).onRun((context) => {
functions.logger.log(`Executed sayHelloWhen with ${scheduleExpression}`)
});
response.send({
status: "success",
data: `scheduled at ${scheduleExpression}`
})
})
})
The problem is pub/sub does not trigger. Other codes are executed.
I would like to have HTTP request body scheduleExpression bring into pubsub.schedule's parameter. I don't want a static schedule expression in corn job.
In client, I would like to define a schedule expression in client side as follows:
function scheduleFunction() {
const functions = getFunctions();
const sayHello = httpsCallable(functions, "sayHelloWhen");
sayHello({ scheduleExpression: "every 1 minute" }).then((result) => {
// const data = result.data;
console.log("Result:", result);
});
}
The example below works only for a static schedule expression, meaning that a cloud function itself has a fixed schedule expression:
exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
console.log('This will be run every 5 minutes!');
return null;
});
It can be exported as cron job trigger and it executes.
But keeping pub/sub cron job function, within onRequest cloud function, as in the first code example, does not execute.
I think it is very interesting what you are trying to do, but I would like to point out that you are missing some steps that could cause your app not to execute the way you need it to.
First, you need to Terminate your HTTP functions
Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might continue to run and be forcibly terminated by the system. See also Sync, Async and Promises.
When you do not terminate them, you might end up in a deeper level in which the following code will not execute unless the previous code has finished. I would also like to say I have not found any application with nested functions like you are doing.
In the Sync, async, and promises page you can find a very explicative video to understand the lifecycle of your functions, and in the How promises work with functions section we have:
When you return a JavaScript promise to a function, that function keeps running until the promise is resolved or rejected. To indicate that a function has completed its work successfully, the promise should be resolved. To indicate an error, the promise should be rejected. This means you only need to handle errors that you want to.
In addition to all this, I would suggest using a separate file to Organize multiple functions for a more organized code, but this is only a suggestion not really necessary.
Finally, I am concerned about the scheduledExpression parameter, I think if none of the above works, you might want to check and share what this value is.

is it possible to keep redux dispatch function in class performing async actions?

I am beginner in Redux and I want to use it for asynchronous logic. Redux style quide recommends to use redux-thunk for it, but it seems I don't need it if I use redux in following way:
class Actions {
constructor(dispatch) {
this.dispatch = dispatch;
}
someSyncAction1(data) {
this.dispatch({
type: SOME_SYNC_ACTION1,
payload: data,
})
}
someSyncAction2(data) {
this.dispatch({
type: SOME_SYNC_ACTION2,
payload: data,
})
}
async someAsyncAction(data1, data2) {
this.someSyncAction1(data1);
await somethingAsync();
this.someSyncAction2(data2);
}
}
// then in my react component:
function MyComponent() {
const dispatch = useDispatch();
const actions = new Actions(dispatch);
//...
return <div onClick={() => actions.someAsyncAction(1, 2)}></div>;
}
It seems to be a simple way but I worry whether it can lead to errors. Please help me to understand what is wrong with it.
This is not very different from the useActions hook referred to in the Hooks documentation - at least for the synchronous stuff.
In the async stuff, you are losing functionality though: Thunks can at any given time access the current state by calling getState.
Also, and this is probably more important: thunks are not only recommended, they are a pattern almost every redux developer knows. So they look at your code and can immediately go to work. Your pattern on the other hand is not established, so it will lead to conflicts if someone other will ever take over your code - without any real benefit.

NGRX bulk effect of already defined single effect

So, Im working on an app with a concept of "Plans" and each plan you can add a comment. That part works fine, but it seems to fail and get confused if i try to run this in a loop.
The Action:
export class AddComment implements Action {
readonly type = CommentActionTypes.AddComment;
constructor(public payload: Comment) {}
}
export class AddCommentSuccess implements Action {
readonly type = CommentActionTypes.AddCommentSuccess;
constructor(public payload: Comment) {}
}
Effect
#Effect()
addComment$: Observable<Action> = this.actions$
.ofType<AddComment>(CommentActionTypes.AddComment).pipe(
switchMap(action => this.commentService.addComment(this.disciplineType, action.payload)),
map((comment: any) => new AddCommentSuccess(comment)),
catchError(err => of(new AddCommentFail(err)))
);
Implementation
What im struggling with is firing this off in rapid success/ I have a situation where I want to add a duplicate comment to multiple plans.
saveSet.forEach(x => {
comment.plan_id = x.id;
this.store.dispatch(this.buildAddCommentAction(comment));
});
For reference:
buildAddCommentAction(comment: DisciplineComment) : Action {
return new CommentActions.AddComment(comment);
}
What is Happening
If i have a list of 5 plans, and want to add a duplicate comment to all of them, Im only getting a successful response for the last item in the loop.
Now i know that is overly chatty, that is 5 separate client/service calls. What I cant figure out, its what the prescribed approach to this should be?
1.) A new BulkAddComment Action, effect, etc. Im loathe to do this becuase I have Comments, Concerns (similar in function and need), and one of each for every "discipline". Thatd be about 36 new effects and twice that in actions. A serious refactor is needed.
2.) Modify the actions and effects for 1 or multiple
3.)?
Thanks for input
This is because you're using the switchMap operator which will cancel the current running observable, in your case the service call.
You'll have to use concatMap or mergeMap. If the order is important use concatMap, if not use mergeMap because this will make your service calls in parallel.
For more info, watch this.

What do chained arrow functions mean in redux?

Looking at the real world example I see this setting up the api middleware:
export default store => next => action => {
const callAPI = action[CALL_API]
if (typeof callAPI === 'undefined') {
return next(action)
}
What exactly is happening here? I see that configureStore is importing whatever that is and passing it to applyMiddleware from redux, but what does this kind of statement mean in js?
I assume it's exporting an anonymous function that returns a function that returns a function? Just tried this:
var a = b => c => d => {
console.log('a:', a);
console.log('b:', b);
console.log('c:', c);
console.log('d:', d);
};
a(5)(6)(7);
// outputs b: 5, c: 6, and d: 7
Function Specialization
The arrow function notation simplifies currying in JavaScript.
Here it's just a way to do partial applications, and permits to bind arguments to the function at different times, by using Closures instead of Function.prototype.bind.
When you call applyMiddleware during Store creation, Redux will specialize your Middleware with the current Store it's been applied to.
Then it becomes a new specialized function, that only takes two arguments:
next => action
Where next is the next middleware that will be called on the Action. (Just like in Express, which popularized the concept, for request handling)
Timeline
The important thing here is that all these function specializations are done at different times.
store can be bound during Store creation.
next can be bound once it knows the Store it's been bound to, so also during Store creation, but could be updated later.
action is known only when you effectively dispatch an Action, which can happen any time.
The specialized middleware (the one which has been bound to the Store, and is already aware of the Next middleware function) will be reusable, and called for each new dispatched Action.
Functional Programming
These concepts (currying and partial application) come from the Functional Programming world.
Redux relies heavily on this paradigm, and the most important thing in Redux is the sidelining of Side-Effects (especially mutations).
Capturing directly the context of the function, or using a global Store via require, is a side-effect as your function will directly after its declaration be bound to this Store.
Instead Redux uses Currying to permit sort of Dependency Injection, and it results in a stateless function, that can be reused and specialized at runtime.
This way your Middleware is Loosely Coupled to the Store.
To understand this clearly you need to first know how middlewares work in redux. So first go through this
Now even after going through the documentation you are still confused, dont worry its a bit complicated, try reading it once again :).I understood this properly after 2-3 reads.
Now the one you mentioned in your question is a curried up ES6 syntax. If you try to convert this to vanilla javascript it would come to something like below
function (store) {
return function (next) {
return function (action) {
var callAPI = action[CALL_API];
if (typeof callAPI === 'undefined') {
return next(action);
}
};
};
};
So if you see its nothing but just chaining of functions.

"Thread safety" in Redux?

Let's pretend I have a long-running function working on computing my new state.
Meanwhile another action comes in and changes the state while the first one did not finish and is working on stuff.
If I am imagining things correctly there is no actions queue and the state might be resolved in some unpredictable manner.
Should I be worried about this at all?
I don't mean real threads, just a concept for the lack of better wording. Actions are asynchronous and state keys are being accessed by reference.
I was concerned about the same thing so I just did some digging. It looks like two threads concurrently calling dispatch() (if it were possible) could raise an exception. But it shouldn't be possible and that error message points to a particular, different cause. The "actions queue" is in the browser's own event loop. That event loop runs async/interaction callbacks (from which we call dispatch()) one-at-a-time.
That's the responsibility of your own action creators and your own reducers, and heavily related to how you structure your actions and reducers conceptually. The Redux FAQ question on structuring "business logic" is very relevant here:Redux FAQ
Thunk action creators have access to getState, so it's very common to have a thunk check the current state and only dispatch under certain conditions, such as this example:
// An example of conditional dispatching based on state
const MAX_TODOS = 5;
function addTodosIfAllowed(todoText) {
return (dispatch, getState) => {
const state = getState();
if(state.todos.length < MAX_TODOS) {
dispatch({type : "ADD_TODO", text : todoText});
}
}
}
Your reducer can also have sanity checks as well:
function todosReducer(state, action) {
switch(action.type) {
case "ADD_TODO": {
if(state.todos.length >= state.maxTodos) {
return state;
}
return {
...state,
todos : state.todos.concat(action.newTodo)
}
}
default : return state;
}
}
Personally, I don't like to have my reducers just blindly merge in whatever data's in the action, unless it's very small (like, say, the name of the currently selected tab or something). I prefer to have a reasonable amount of logic in my action creator to set up the action, a minimal-ish amount of data included in the action itself, and a sufficiently smart reducer to do the work based on that action.

Resources