Im in React v16, Redux, saga 1.1.3, AWS cognito for UserMgmt
Requirements:
after creating a new user, email them a password reset link so they can log in and set their password
password reset action works perfectly
new user creation works perfectly
I need to call the pwd reset action once the new user is created. I'm targeting the saga as a good place to make this call. Once all the yields for the existing saga are done, right before setting state with put, i make one more yield call to the other action i want to run...and never see it do anything : )
is there a trick to running or dispatching actions from sagas.
this is the one line i need to run
cognitoActions.forgotPwd(email);
running it from my front end works every time.
also, it is imported
import { cognitoActions } from "fakepath/cognito";
on the front end mapping the action to props, passing it to the component, then calling it works every time...
just no action from the saga. i've tried logging results and dont think its running at all, no console.log happens...
You can use the "put" function to call it from within an effect like this:
yield put(action)
Potentially depending on what you want you can use:
putResolve(action)
You can find documentation for this here:
https://redux-saga.js.org/docs/api/
Related
I have the following pattern on my single page app (React + Redux).
It runs every time a load a page on the app. User navigates to a specific page, and the loadPageThunk is dispatched. The initial state of the page shows a spinner to the user. This is used for example, in a blogpost page.
That thunk will get some async data (the blogpost), and then will show the page with that data.
It works fine. When the user navigates away from the page. A useEffect dispatches a RESET action to reset the state back to its initial value.
My question is:
What if the async call takes too long to complete and the user navigates away? It will create a problem because now there's a pending promise that will complete in an unexpected time. How can I prevent that completion from updating my state?
Imagine the following steps for an async call that is taking 10 seconds to complete:
#### FIRST PAGE LOAD ####
USER VISITS blog/slug-1
loadPageThunk() IS DISPATCHED
blogPost1 STARTS GETTING FETCHED (WILL TAKE 10 SECONDS)
USER NAVIGATES AWAY
#### SECOND PAGE LOAD ####
USER VISITS blog/slug-2
blogPost2 STARTS GETTING FETCHED (WILL TAKE 10 SECONDS)
USER IS STILL SEEING SPINNER
blogPost1 (FROM THE PREVIOUS VISIT) HAS COMPLETE AND WILL UPDATE THE STATE
USER NOW SEES blog/slug-2 WITH THE DATA FROM blogPost1 WHICH IS AN ERROR
blogPost2 WILL EVENTUALLY COMPLETE AND USER WILL SEE A CONTENT FLICKER ON THE PAGE
QUESTION
How can I avoid pending promises that are no longer useful from being able to update the state?
This problem is not currently happening in my app, but I think that a good design should account for that.
Should I add an ID for my LOAD_PAGE cycle, so I can check the ID of the current cycle before allowing callbacks / async code from updating the state when IDs don't match? How do people usually handle this?
Personally I store blog data as entities (posts, comments, etc.) keyed by id and collections. The collection is just the array of post ids on a particular page.
For example,
{
entities: {
posts: {
1: {...},
2: {...}
},
comments: {
123: {...},
999: {...}
}
},
collections: {
"blog/slug-1": [99,98,97...],
"blog/slug-2": [89,88,87...],
}
}
This sort of structure means that every page can save its data in the correct place regardless of whether it is the current page or not. It also means that every page can select its own data and can see whether that data already exists in the state.
The promise returned by createAsyncThunk has an abort() method attached which can be used to 'cancel' the promise. See canceling while running. You can call abort() in your cleanup to prevent the thunk from being fulfilled.
In your reducers, if you are handling the rejected case for your thunk, then you can add an exception for cases where the error name is AbortError to do nothing instead.
To expand a bit about your specific situation: a good rule of thumb is that if you find yourself 'resetting' state when you unmount the component, then it should have just been local component state in the first place.
How can we get the data from the saga directly into our component ?
or
Is this a pattern we should not follow and directly make a service/ api
call from the component using some service layer.
I have been following this issue on git on if this is possible there are too many permutations and combinations and i am a bit confused ..
I tried this small example by refering this
stackblitz.
In this case when i try and return this
function* helloSaga() {
return new Promise(function(resolve, reject) {
resolve('start of new Promise');
});
}
and access it like this
let response = dispatch(action('SHOW')).then(data => {
console.log(data); // i cannot get this to work say if this were a api response .
})
Nothing happens .
Is this pattern acceptable if yes then how can we make it work and what am i missing it
And if this pattern is an anti-pattern then making service calls from a layer like getData() should be enough from componentDidMount() .
A generator function doesn't really return a value (or the promise in your case) as you'd expect with any "regular" function. It returns a generator-object that you can pause, continue, cancel etc.
Check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
The way to go with redux-saga in a web app would be:
Trigger an action like "SHOW" in your example
Have a saga take your action "SHOW"
The saga would then do the async API request
Once the API request is done, the saga dispatches another action (something like FETCH_SUCCEEDED
Handle the action in your reducer and store the response in the redux store.
Once it's stored in the redux store, you can select it from any react component you want to (use connect from redux for that)
Here's your stackblitz with a minimal example:
https://stackblitz.com/edit/react-redux-sagas-demo-app-4tpevb?file=index.js
For a production app, you'd want to wrap the API request in a try{..}catch(){..} and handle the error with a proper action like FETCH_FAILED and display an error message in your FE (if it effects the user).
Now I check by using 'componentWillRecieveProps' to check 'nextProps' of Redux state is it finish or not.
like this example,
if (!nextProps.a.update && !nextProps.a.error &&
nextProps.a.update !== this.props.a.update) {
const oldData = [...this.props.a.data];
const newData = [...nextProps.a.data];
.
.
.
});
}
}
Did anyone have another good idea to check it?
Thanks.
if you are using any middleware like Redux-saga or thunk you can check whether update is done or not from the middleware itself and once its updated you can dispatch an action from middleware itself and get notified.
Not sure how much it will be appropriate for your case.
It really depends.
1. If the logic your'e trying to implement, is only for same component, and from one dispatched action only at a time, then using a flag is definitely OK, set true on 'will update' and false on 'did update' (same for mount if necessary).
2. If the logic is upon few actions, then maybe create a bulking mechanism using promise will be better. Again, depends on your logic. Maybe it will be enough to use flags for each type of update.
3. If the logic is system wide, i would use a global variable, maybe even set on 'window'. For example, i wanted to implement a variable called 'isLoading', which will define if there's any pending ajax call.
Please let me know if that's what you've been looking for.
I would like to have a two part submit strategy for my redux-form. First, the user called submit on the form which calls a validation method. The response might have some warnings. I want the user to see the warnings, if any, and optionally continue with another submit that will be a real POST to the server rest api.
If there are no warnings, I would like the component to submit automatically. I am trying to kick this off from the componentWillReceiveProps method.
The problem is that nextProps.handleSubmit(this.doSubmit2of2); does not call this.doSubmit2of2. Execution just steps over that call.
componentWillReceiveProps(nextProps) {
//boolean that indicates validation just occured against the server
if (nextProps.storeWithValidation) {
//the user hit submit, first it was validated, if no issues, go ahead and try to create
if (nextProps.storeValidationOk) {
//fire off create store
nextProps.handleSubmit(this.doSubmit2of2);
}
else {
//there are validation issues of some kind, let the user see them
//do nothing here and let the render method do its thing with the props
}
}
}
I have found the discussion here: https://github.com/erikras/redux-form/issues/67, but in my case the submit is happening as result of a particular server response. Also, I realize that there are validation features of redux-form. Am I designing too far outside of the intended framework convention?
Also, I have thought of redesigning my server api, but I would like to know how far I can go with this current approach of automatically resubmitting after a response from the server.
From what I understand you want to submit the form remotely after a response from the server. You can create a remote submit following this example from the docs. Then you can dispatch(submit('yourFormName')) whenever you want to as many times as you want to.
I want to be able to make an API call in a Flummox action and transition differently depending on the response. I can pass the router into the action call but am looking for advice on a potentially better way.
UPDATE:
The correct answer is below but I wanted to add some detail to this.
I'm doing an isomorphic app that 1. needs to get data from an api and 2. may need to redirect based on the api response. Whatever I do needs to work through an express.js app and through react.
I made a small lib that does the api call and return some results. I pass it an object (query params object from express for the server-side or a similar object I create for the react-side). This lib makes the request, determines if a redirect is needed and passes back errors, path (string), redirect (boolean), and data (json).
In express, if the redirect boolean is true, I just redirect to it with the current query params. If it's false, I pass the data to flux through an action which updates a store. I then renderToString with react, serialize stores so the clint-side can bootstrap, and send a rendered page to the client.
In react, the redirect boolean isn't important, I get the response back from my lib, pass the data to my flux action, and just transition to whatever the path is. There's really no notion of redirection. Just go to the path no matter what.
Hopefully this is helpful to someone.
In my setup I have my own router module which just wraps the instance of react-router that I create at startup. That makes it easy for any part of the application to just require that module and do what it needs to.
But I would advise you not to have side effects like a call to the router inside your actions. Actions should concern themselves on mutating your application state, and nothing more. It should be possible to call the same action from anywhere in your application which needs to perform the mutation that the action encapsulates.
So if you're switching routes during an action, you're basically tying that action to that particular use case. Let's take an example. You have a todo list, with an input box at the bottom to add a new todo. For that use case, it might be useful to switch route after you saved the todo. Perhaps you switch to Recent Todos or something. But then a new use case comes along where you want to be able to add new todos during another workflow, perhaps the user is planning her week and should be able to add todos on different days. You want the same action that adds a todo, but you certainly don't want to switch routes because the user is still planning the week.
I haven't used Flummox a lot, but from my understanding your Flux object returns whatever the action returns when you trigger an action. So instead of putting the route change inside your action, make sure to return the response from the action and let your component decide if the route should be changed. Something like this:
// todo-action.js
class TodoActions extends Actions {
createMessage(todo) {
return TodoStore.saveToServer(todo);
}
}
// todo-list.js
const TodoList extends React.Component {
render() {
...
}
addTodo(todo) {
this.props.flux.addTodo(todo).then(response => {
if (response.some.prop === someValue) {
this.props.router.transitionTo(...);
}
});
}
}
That way, the action is still nicely decoupled from the route change. If you want to do the route switch in more than one place, you could encapsulate that in a addTodoAndSwitchRoute method in your Flux class.