Should my reducer handle ##redux-saga-test-plan/INIT action? - redux

I'm using redux-saga-test-plan to test my sagas for redux-saga. While debugging my tests I've noticed that my reducer receives one unexpected action, with type === "##redux-saga-test-plan/INIT".
I do not see any mention of this ##redux-saga-test-plan/INIT action type in redux-saga-test-plan documentation.
What is the purpose of this action? Should I handle it in some special way?

A reducer is a function that, when bound to the store, will be called regardless of what action has been dispatched, so it always has to accommodate for unexpected action types. By far the most common way to do this is to do nothing, which in (state, action) -> state kind of function signature means just returning the state itself without any changes:
switch (action.type) {
case ABC: do something; break;
case XYZ: do something; break;
default: return state; // <- default "response" to actions that aren't handled by code above: stay chill, do nothing, return the state as is
}
The action ##redux-saga-test-plan/INIT is internal for the redux-saga-test-plan lib. Because there is no way an action, once dispatched, can be skipped or hidden from the workflow (or developer tools), you might have seen many actions of types that look similar to that one. Don't worry, those are most likely used by libs themselves and don't require any specific handling from your side.

Related

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);
}

Can I pass always the full state to reducers?

Is there any inconvenient at all if I design my reducers to, instead of reading only the partial state, had access to the full state tree?
So instead of writing this:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
I destructure state inside doSomethingWithA, c or processB reducers, separately:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state, action), // calc next state based on a
b: processB(state, action), // calc next state based on b
c: c(state, action) // calc next state based on a, b and c
}
}
Would I'd be using more RAM? Is there any performance inconvenient? I understand that in javascript, a reference is always passed as parameter, that's why we should return a new object if we want to update the state or use Immutable.JS to enforce immutability, so... again, would it be of any inconvenient at all?
No, there's nothing wrong with that. Part of the reason for writing update logic as individual functions instead of separate Flux "stores" is that it gives you explicit control over chains of dependencies. If the logic for updating state.b depends on having state.a updated first, you can do that.
You may want to read through the Structuring Reducers section in the Redux docs, particularly the Beyond combineReducers topic. It discusses other various reducer structures besides the typical combineReducers approach. I also give some examples of this kind of structure in my blog post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.

#ngrx/store Ignore first emitted value

store.select() emits previous store state.
Is it possible to subscribe to changes from "this point forward" without getting the previous store value?
If you are not interested in the first emitted value, you should be able to use the skip operator:
store.select(...).skip(1)...
skip operators need piping now, you can use skip like this:
store.pipe(select(...), skip(1));
In terms of the 'hacky' part, it is a standard practice in ngrx to set an initial state with properties set to null. and that value gets emitted initially. so the first value you get will be null in these cases.
Alternatively you could also consider skipwhile(https://www.learnrxjs.io/learn-rxjs/operators/filtering/skipwhile) and use it like this:
store.pipe(select(...), skipWhile(val => val === undefined));
where undefined is the initial value of the property you are interested in. Rather than setting the initial value of the property to undefined, you could use null as the initial value as well, and change the above skipwhile() accordingly.
Just sharing my thoughts (and solution) after reading #Niz's answer.
This is a perfect, practical example of how to utilize the difference between null and undefined. When you initialize your state with null, you're basically saying:
I don't care about differentiating the nullable future state from the
initial one. I don't care if the user is null because he has signed
out or because he just didn't sign in
However, in some cases this could be insufficient. Think about a case when you need an asynchronous call (implemented in effects) in order to know if you have an active user session. Based on the selection result, you should determine whether to show a login modal or redirect to a content page. With initial user state set to null, you'd pop up that modal and then immediately hide it when that asynchronous call returns a session value.
With initial state set to undefined you can make that differentiation, saying:
Initially, I know nothing about my state, then it's undefined. When I know it should be empty, then I'll set it to null.
Therefor, as a practical solution, I set everything on the app's initialState to undefined. In the example above, I need to know if the login modal should be displayed after the asynchronous call resolves. skipWhile(val => val === undefined) will do the job for sure, but repeating it over and over again feels a little tedious. Plus, it's not really descriptive to our use case. I created a rxjs-custom-operators.ts with a shortened implementation:
import { Observable } from "rxjs";
import { skipWhile } from "rxjs/operators";
export const skipInitial = () => {
return <T>(source: Observable <T>): Observable<T> => {
return source.pipe(skipWhile(value => value === undefined));
};
};
Usage:
navigateOnLoad(): void {
this.store.pipe(select(selectAuthUser), skipInitial()).subscribe((authUser: CognitoUser) => {
// Navigate to login if !authUser, else navigate to content...
});
}

"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.

Reactjs/Flux : When actions depend on async actions

Reactjs 0.14.0 , Vanilla Flux
Async Actions dependencies are a constant conceptual struggle. I've looked at this for months(dead serious) and every similar thread just doesn't make plain what I think is one of the hardest parts of the React/Flux learning curve.
The Problem:
If you want ActionB to be carried out one time directly after ActionA is done it's not really obvious at all where to put that to be updated by the View as the Flux pattern suggests( cause supposedly ActionA->ActionB chaining is an anti-pattern)
Note: maybe componentDidUpdate is the best that can be done, but it implies that ActionB can be called many times needlessly.
What I'm Trying to Do
So I'm using the common ActionA->WebAPI->Action->Dispatcher->Stores->View->ActionB
Which in most cases flows like this:
ActionA->WebAPI->Action(Async)->Dispatcher->Stores->View->ActionB
And often it is the case that ActionB is dependent on the Payload data of ActionA to be ready in it's store.(waitFor() was not designed for async situations like this supposedly.)
Example:
Main.js
componentWillMount: function(){
AuthActionCreators.checkForSession((loggedIn) => { //THIS IS A CALLBACK TO DO AN ACTION DEPENDENT ON USER DATA BEING IN THE USERSTORE
if(loggedIn){
AnotherActionCreators.updateAnotherStoreNowUserStuffIsInStores(this.props.someProp);//Action->Action(supposedly an anti-pattern)
}
});
},
AuthActionCreators.js
//Problem #1 Pointless Middle-Men Actions When Using The Pattern: ActionToServer->WebAPIUtils->ActionWithPayload
//Note: Many say to not call WebAPIUtils directly in Components
checkForSession: function(callback){
/* Notice how this action SEEMED to not need the dispatcher
because its a call to a server and I wait for a return to call an Action
that can actually dispatch a payload)*/
WebAPIUtils.hasSession(callback);
},
WebAPIUtils.js
//Problem #2 Async Actions calling dependent Actions
//ActionA -> ActionB is supposedly an anti pattern instead of :ActionA -> Dispatcher -> Store -> View -> ActionB
var hasSession = function(callbackDepOnUserData) {
let jwt = localStorage.getItem('jwt');
if (jwt) {
$.ajax(this.href, {
success: function(userData) {
ServerActionCreators.receiveUserPayloadInStore(userData);//Async Action that will actually sends a payload(I'm kinda okay with this action)
callbackDepOnUserData(true);//This callback(which is an action)feels like an anti-pattern but it the only way to call after dependent data is
//available in store
},
});
}
else{
console.log("does not have a session");
}
}

Resources