I have 2 actions onBoardingUserStart and authenUserSuccess, I want do authenUserSuccess after finishing onBoardingUserStart. But maybe it do at same time.
successMeta = tokenObj.nickname ? onSuccess : onSuccess;
successActions.push(
//
actions.onBoardingUserStart(),
actions.authenUserSuccess({
...res,
...tokenObj,
}),
actions.getMyDetailInfoRequest()
);
Related
When a user logs out, we get a number of errors in our components due to the redux store being cleared out before you are taken to the login page.
We have some components that use the useQueryWithStore. Here is an example:
const { data, total, error, loaded } = useQueryWithStore({
type: "getList",
resource: "tasks",
payload: {
pagination: { page: 1, perPage: 5 },
sort: { field: "date_end", order: "ASC" },
},
});
Then we are checking whether the data is loaded and there are no errors before rendering the component.
return (
{!loaded && !error ? (
<Loading />
) : (
...our component rendering data...
)
)
The problem is that when a user logs out, it first clears the redux store before navigating to the login page. That means that both !loaded and !error are true and it attempts to render the component. But because the redux store has been cleared, data is undefined.
How would you handle this best?
Add default value to data? - const {data = [], total, error, loaded}
Check if data is undefined? - {!loaded && !error && data}
Use optional chaining? - Any references to data could use optional chaining - data?.map(...map function...)
Make pull request to change order of logout? - Instead of clearing redux first we could navigate to login page or logged out page and then clear redux store.
A better way I haven't thought of?
Thoughts?
All of my API calls are handled by redux-sagas. I'm creating a heartbeat modal in my app to detect inactivity. Each time a saga goes off I want to clear my setTimeout so I know that the user is active.
My middleware is a basic one at the moment:
const heartbeatMonitor => store => next => action {
if (action['##redux-saga/SAGA_ACTION']) {
clearTimeout(window.myTimeout);
}
window.myTimeout = window.setTimeout(function() {
// send off an action to tell user they are inactive
}, 100000);
}
It seems like looking for this symbol, ##redux-saga/SAGA_ACTION, is the only way to tell if the action is a saga. I see that redux-sagas has a createSagaMiddleware(options) and I tried using effectMiddlewares but it doesn't seem like you have access to the dispatch method in there so I can't send off a new actions.
but it doesn't seem like you have access to the dispatch method in there so I can't send off a new actions.
Not sure whether this is the kind of solution you wanted, but you do have access to the dispatch method where your comment // send off an action to tell user they are inactive is located in your code snippet, as it is exposed by the store object. (this is documented in the Store Methods Section of the store in the redux docs)
Therefore something like the following should satisfy your case:
const heartbeatMonitor => store => next => action {
if (action['##redux-saga/SAGA_ACTION']) {
clearTimeout(window.myTimeout);
}
const { dispatch } = store;
window.myTimeout = window.setTimeout(() => {
dispatch({ type: "USER_INACTIVE" });
}, 100000);
}
Note: I would probably implement this differently (using redux-sagas effects) Maybe this is an option for you too:
Example Saga
import { put, delay } from "redux-saga/effects";
function* inactiveSaga() {
yield delay(100000);
yield put({ type: "USER_INACTIVE" })
}
Example Integration of saga above:
(add the following in your root saga)
//import { takeLatest } from "redux-saga/effects";
takeLatest(() => true, inactiveSaga)
Explanation: Every action will trigger the inactiveSaga (cause () => true). The inactiveSaga will wait 100000ms before dispatching the "inactive action". If there is a new action within this waiting time the previous execution of the inactiveSaga will be canceled (cause takeLatest, see redux-saga effect docs for takeLatest) and started from the beginning again. (Therefore no "inactive action" will be sent and the inactiveSaga will start to wait for these 100000ms again, before being cancelled or completing the delay and dispatching the "inactive action")
We are integrating the NGRX library in a project at the company where we work and we want to perform optimistic updates at the front-end instead waiting for the server response to perform some action. What we have tried is to use the startWith operator, but it throws the Action properly and then, as the releaseService.deleteRelease method does not return an action, it throws the invalid action: null error.
We have tried to add the {dispatch: false} config to the #Effect, but then the first action is not thrown...
We have also though about using a tap oeprator and dispatch some action directly to the store, but we consider it an anti pattern.
So, is it possible to achieve this without creating an splitter intermediate action? Thanks.
#Effect()
deleteRelease$ = this.actions$.pipe(
ofType(ReleaseCardActions.ReleaseCardActionTypes.DeleteRelease),
exhaustMap((action: ReleaseCardActions.DeleteRelease) => {
return this.releaseService.deleteRelease(action.id).pipe(
startWith(new DeleteReleaseSuccess(action.id)),
catchError(() => of(new ReleasesApiActions.DeleteReleaseFailure()))
);
}),
);
Maybe I don't understand the question, but why not perform the optimistic update on the DeleteRelease action directly in the reducer, so your reducer and effect will fire on the same action independently.
Then, you can do the "real" update from the response coming from the effet.
#Effect()
deleteRelease$ = this.actions$.pipe(
ofType(ReleaseCardActions.ReleaseCardActionTypes.DeleteRelease),
exhaustMap((action: ReleaseCardActions.DeleteRelease) => {
return this.releaseService.deleteRelease(action.id).pipe(
map(new DeleteReleaseSuccess(action.id)),
catchError(() => of(new ReleasesApiActions.DeleteReleaseFailure()))
);
}),
);
I'm using ngrx in an Angular project. In this example I have an array of requests. I want to dispatch an action after each request but also after all are done.
So far I have something looking like this:
Observable.forkJoin(requests).pipe(
map(() => new actions.requestsSuccessful()),
catchError(() => of(new actions.requestsFailed()))
);
where requests is an array of Observables.
The code above works fine, when all requests are done, my requestsSuccessful() action is correctly dispatched.
However, I'm implementing a progressbar, which I want to update after each request has been made, but I also want to keep the dispatch of the action where all requests have been made.
I can't figure out how to dispatch an action after each request while keeping the action when everything is done.
Any ideas?
forkJoin emits only when all Observables complete so it's not useful here. Instead, you can use concatAll and concat.
This is model example simulating what you want if I understand you correctly.
const makeRequest = (v) => of(v)
.pipe(
delay(1000), // Simulate delay
map(response => ({ action: 'WHATEVER', response })), // Map response into action
);
const requests = [makeRequest(1), makeRequest(2), makeRequest(3)];
from(requests)
.pipe(
concatAll(), // Execute Observables in order one at the time
concat(of({ action: 'ALL_DONE' })), // Append this when all source Observables complete
)
.subscribe(console.log);
See live demo (open console): https://stackblitz.com/edit/rxjs6-demo-zyhuag?file=index.ts
This demo will print the following output:
{action: "WHATEVER", response: 1}
{action: "WHATEVER", response: 2}
{action: "WHATEVER", response: 3}
{action: "ALL_DONE"}
Btw, in future RxJS versions there will be endWith operator that you can use instead of concat that makes it more readable. https://github.com/ReactiveX/rxjs/pull/3679
Haven't tested it. Maybe this works.
let progress=0
Observable.forkJoin(requests.map(e=>e.do(()=>progress++)).pipe(
map(() => new actions.requestsSuccessful()),
catchError(() => of(new actions.requestsFailed()))
);
I am trying to dispatch an action in a saga function in this way:
yield put(addToCart(item));
When trying to execute it, it gives the error __webpack_require__.i(...) is not a function.
"addToCart" is an action creator which I imported:
export const addToCart = product => ({
type: types.ADD_TO_CART,
payload: { product },
});
This action never fires.
The item (or product) is an object, like :
{
'id' : 5,
'thing' : 'stuff'
}
(+ other properties).
I can dispatch other actions, but this one doesn't work for some reason.
I had imported the action creator wrongly:
import addToCart from './actionCreators'
It should be:
import { addToCart } from './actionCreators'
But what kind of error is "__webpack_require__.i(...) is not a function" ? The error messages are worthless, they give you no clue at what the error is about.