Questions about Redux Thunk Middleware - javascript

i really need help.
I'm confused about how should i manage my async API Calls and error handling using redux thunk middleware.
Option 1
Send essential info to redux thunk middleware (newJobInfo) and ALSO other functions to manage my component state (for example, disable submitting button while making api call)
Error handling: Inside redux thunk middleware using try { } catch { }
Option 2
Keep this functions inside my component and only send essential data to redux thunk middleware
Error handling: Remove try { } catch { } of redux thunk middleware (or keep it and throw new Error inside catch statement) and manage errors with .then() and .catch() inside my component.

In this case, I would think how do I use this action in my project.
For example, if I use need to call 'startUpdatingJob' not only with navigate effect, I will use option 2, otherwise, if I only use it once, it is acceptable to use option 1.
But in general I would rather use option 2, in this case we keep action logic more consistent, as it has only 1 task to complete.

Related

Structuring calls to update global and local state

Some of my components doesn't want to store all state globally. Two examples:
messages component: usermessages are fetched and stored locally because they are only needed for the current component. But when they could not be fetched (api error), the error should be dispatched to global state (vuex).
buy component: 'recent buys' are fetched and stored locally, but 'money' should be dispatched to global state, and error too when recent buys could not be fetched.
I'm currently figuring out how to structure this and I need some help. I have a directory services which includes calls to my api. Let's take the buy service as an example:
/services/buy.js:
// here code to dispatch money state
// here code to dispatch 'last activity' state
Vue.$http.get('/buy', credentials)
.then((response) => {
// return recent buys
})
.catch((error) => {
// here code to dispatch error state
});
There are some logics between the services as well: For example, after a succesful buy, a new message should be sent from /services/newMessage.js
But how and where should I structure all of this? Let's take the buy component as an example. I see a couple of options:
#1: This is the code above
The buy-component imports the buy service and calls it: newBuy()
The service dispatches the money to global store, and the service gets the recent buys and returns them
Back in the component, it updates the local store with the returned value from the service
The component has the logic too: after a succesful return, it calls the message service to send a new message: sendMessage()
#2: The difference with #1 is that the logic takes place inside the service
The component imports the buy service and calls it: newBuy()
The service dispatches the money to global store, and imports the message service
The message service sends a new message: sendMessage()
Back to the buy service, the recent buys are fetched and returned.
The component now updates the local store with the returned value
#3: The difference with steps above is that all actions related to Vuex are inside a special actions.js file, so it is a clear separation of global and local state updates.
The component imports the buy service and calls it: newBuy()
The service imports ./store/actions.js and calls the updateMoney() service which updates the money
Goes further with the steps from #1 or #2
Could someone please help me out? How to combine components that use both global and local state? Are one of the three steps above the right way to do that?
In short, based on your situation: option 2
For me if there is no need for a state to be shared globally then all you are doing is polluting vuex's states by writing everything to it.
If for instance you had 10 components that functioned like the buy component, and each of those pulled an individual state only they needed from your vuex store, then you will be making the vuex store harder to reason about.
Furthermore if you start attaching actions and mutations for those states, then you'll likely need to build modules for each of the 10 components, again obscuring your state and logic.
Therefore in this instance option 2 seems a far better way to go if you are sure you won't need the state you retrieve elsewhere. You seem to have a pretty good grasp on why you would use vuex so that puts you in good stead. I would say that half the work with larger applications is in the planning. Therefore if you can map out how your app will function and see before you build where the connections need to be, and in turn where a components data is completely isolated, you should be able to quickly make those decisions on what you do and don't push to vuex.
In terms of the choice between option 1 & 2 I would say this again comes down to a question of scope and keeping things DRY. If every time you are returned data from newBuy you have to call sendMessage and you have the data in buy-service to populate the message, then your services should work together. It's fine that they do so, after all you are no doubt writing the message-service in a manner that decouples it from any dependancies outside those for sending messages. Therefore if the buy-service is written in a similar fashion it can pull that in and utilise it.
With the above in mind Option 1 therefore appears to be duplicating a function which would need to be run every time the buy service is called. For that reason I would avoid it in case in the future you want to expand things, as your app should be far easier to reason about if dependant functions are not replicated in various places. Instead you would look at newBuy and see on it receiving its data, it calls sendMessage and therefore updating is simple and the logic is clear.
To provide a little more context, I'd look to run the various stages like below:
The component imports the buy service and calls: newBuy()
Calling newBuy() should return a Promise to the component
The buy service imports the message service
The buy service fetches the data, i.e. newBuy calls getMoney and getRecentBuys.
Both of the above return a Promise, now you use Promise.all to wait for the 2 endpoints to resolve and pass back their data.
On resolving of the newBuy Promise.all:
getMoney returned data: the buy service dispatches the money to vuex modules store
The money store could be held within a vuex module if you have various types of data within this store. It would help make its state, actions etc.. easier to work with
The buy service calls the message service to send a new message: sendMessage()
The buy service resolves its Promise
pass the recent buys as the payload
Promise is resolved on the component which now updates its local data with the payload
On rejecting of the newBuy Promise.all:
The buy service rejects its Promise
pass an empty payload or message
dispatch error to vuex store
Promise is rejected on the component so component knows not to update its local data

Accessing Store within Redux and React Native app

I'm starting to develop an app that will be making calls to an API that will require the inclusion of a JWT that I plan on storing within the Redux store.
Typically you can access the Redux store by mapping a particular part of the store to a component's state with mapStateToProps() and connect(), however I'd like to access the Redux store within a file that will not end up being a React component/container - just a pure js file that will be handling the API calls.
Is this possible?
You can use the getState() function of the store returned by createStore. You can use this inside a function that fishes the needed data from the state.
const myImportantData = () => store.getState().my.deep.data;
The version above uses the store directly, as in a global variable. This prevents you from using more than one store in your process. Normally this is what you want, but this isn't true when running on the server. That said, on the server you'll likely not need the JWT access anyway.
If you do need to swap stores, however, then you can pass it as a parameter or close over a store local variable:
const getDataGrabber = store => () => store.getState().my.deep.data;
Yes, it is possible.
Redux is framework agnostic, although it was made with React in mind.
The mapStateToProps() and connect() methods aren't part of the core Redux library but of the helper library 'React-Redux' which provide bindings specifically for Redux use within React.
It's worth checking out Dan Abramov's Getting started with Redux series to get the principle of how the guts of Redux works.
An example of which are Video 6 and Video 7 where he discusses the Redux store and describes the three methods that make it up:
store.getState // gets the current application state
store.dispatch // changes the current application state by dispatching an action
store.subscribe // suscribes to changes and re-renders the app using the
// current state
The videos go into a fair bit of detail as to how to register the reducers with the store, so the series should prove very helpful.

How to to cancel pending asynchronous actions in React/Redux

Consider the following situation:
When user navigates to page, two asynchronous Redux actions are dispatched to fetch two sets of related data parallel. If either of these fetches fails, it will be detected by the component, which will render error component on next cycle, which in turn dispatches clearState action when mounting.
However, the other action is still to be resolved/rejected and will spoil the clean state. Objective is to interrupt this (and preferably many other) pending asynchronous actions, when clearState action creator is called. AFAIK, pending promise should either way resolve/response, no matter what.
First thing to come in my mind is to introduce INTERRUPTED flag to each reducer, but I can't wrap my head around how to use it to make following SUCCESS/ERROR action not to affect / return the default state.
How to do this, keeping the complexity at minimum?
I had a similar problem, I'm using jquery for making ajax request and redux-thunk middleware.
The solution is to make your action creators return the promise, this promise will be returned by the dispatch function, then you'll be able to abort it.
In my action creator I have something like this :
function doSomething() {
return (dispatch) => {
return $.ajax(...).done(...).fail(...);
}
}
Then in my component I have :
componentDidMount(){
this.myPromise = this.props.dispatch(doSomething());
}
somefnct() {
this.myPromise.abort();
}
Also have a look at this comment by Dan Abramov.
I was recently faced with the same problem, in which I had to cancel pending or stale async redux actions. I solved it by creating a cancel redux actions middleware.
In redux we have the concepts of middlewares. So when you are sending the request it will go through a list of middlewares. Once the api responds back its response will pass through a number of middlewares before it reaches redux store and eventually your UI.
Now suppose we have written a cancel middleware. The api request will go through this middleware when it being initiated and the api response will also go through this middleware when the api responds back.
Each api request in redux is associated with an action, each action has a type and payload.
Write a middleware, which whenever an api request is done stores the action type associated. Along with it, it stores a flag which states whether to discard this action. If an action of similar type is fired again, make this flag true which says discard this action. When the api response comes for the previous action since the discard flag for this api request has been set to true, send null as response from the middleware.
Look at this blog for detailed information about this approach. https://tech.treebo.com/redux-middlewares-an-approach-to-cancel-redux-actions-7e08b51b83ce

Redux app business logic that doesn't affect the state?

I have an application that utilises Oauth2 "redirect flow" to authenticate users, i.e. users are redirected to another website to log in, and are then redirected back to my site.
The documentation and other sources all seem to claim that business logic should go either in the action creators or in the reducer. However I have a login() function that does not change the state in any way, what it does is that it saves the current state of the application as a plain object in localStorage, then redirects the user to the authorisation server. When the user has logged in and is redirected back the state is restored (by another piece of code) and fed as the initial state to my store creator function.
My question: The logic of the login function just retrieves the state but doesn't change it in any way so it doesn't belong in the reducer. It also doesn't return an action which is the definition of an action creator. Where in my application structure should I put it?
Is it "ok" to have an action creator that actually doesn't create an action? I'm doing this right now with redux-thunk (because I need to getState()) and it works without any problems, but it feels wrong because it's actually not an "action creator", on the other hand I also have a logout() function that does return an action so it feels like they should live in the same place. I guess it's kind of a corner case, but not really because I can think of a lot of reasons to save the state in this way and redirect visitors to other sites (or even just save the state without redirecting).
PS. I know there are libraries to sync my redux store with localStorage automagically but this is not exactly the point of the question.
EDIT: Some clarification:
From the Redux docs regarding where to put business logic:
There’s no single clear answer to exactly what pieces of logic should go in a reducer or an action creator.
Reading on, it's made clear that there are only two places where I'm supposed to put business logic, either in a reducer or an action creator. Now, since what I want to do doesn't affect the state and a reducer by definition affects the state, I'm leaning towards putting this logic in an action creator.
The docs on action creators: Action creators are exactly that—functions that create actions. It's easy to conflate the terms “action” and “action creator,” so do your best to use the proper term. [...] In Redux action creators simply return an action
The docs on actions: Actions are plain JavaScript objects.
The pseudocode for my login example:
function login() {
// retrieving and serializing the state, then:
localStorage.set('my_app_id_state', my_serialized_state);
window.location = url_to_authorization_service;
}
This code obviously has no place in an action creator because I'm not returning an action, which is the purpose of an action creator. However I still need to retrieve the state so I can't make it completely independent either.
So again, the question is where do I put this piece of code in my application structure?
Again, the code works and everything is fine, so I guess this is more of an academic question, but it bugs me a LOT that I'm clearly breaking the essential rules of Redux here. Maybe it's just an exception?
This was a good a question and made realise I was doing something wrong. So hopefully this answer will be helpful at least to both :)
Take this example adapted from the Real World Redux example:
A private function
function oauth(url) {
return (dispatch) => {
dispatch({
type: types.OAUTH_LOGIN,
url
});
};
}
is called by the action creator (which returns a function)
export function loginToApi(redirectUrl) {
return (dispatch) => {
return dispatch(oauth(redirectUrl))
}
}
The action in turn is then called in the component, like so:
login() {
return this.props.dispatch(loginToApi());
}
So one way to answer your question is to put your business logic inside a private method, which is then called by the action creator.
There's probably a more elegant way, but this is how is done in the docs I mentioned above.
Regarding your login logic for storing the state in local storage: I don't think you need this. Redux store is managing the state for you.
Dan Abramov replied on twitter, I will settle on the conclusion that this is an exception to the rules outlined in the documentation.

Dependent Actions in Flux and React JS

I'm currently having an issue in our Flux app where a component needs to first fetch the current user, and, if and only if the current user is fetched, fetch that user's notifications using a different Ajax call, as below:
_onCurrentUserChange: function() {
this.setState(getStateFromStores());
NotificationsActionCreator.getNotifications();
},
As you can see, I'm trying to dispatch the action to getNotifications after we know the currentUser has changed.
However, Flux doesn't allow for multiple dispatch of actions, and we've decided that all server requests should be dispatched through actions.
Thus, there's no way for me to wait on the currentUser to be received, and then dispatch the action to fetch the notifications.
What would be the correct Flux way to do this?
So I figured I'd post the way I fixed this for other people to see.
Basically, we ended up creating a function in our WebApiUtils module that makes sequential Ajax calls to the getCurrentUser and getNotificationsForCurrentUser backend routes. Then, we dispatch an action that contains both the currentUser and the notifications. The currentUser and notification stores both listen for this event, and each grabs the data it requires.
If you want to see the code for this, check out:
https://github.com/krazemon/repcoin/blob/master/js/api.js#L16
https://github.com/krazemon/repcoin/blob/master/js/stores/AuthStore.js#L59
https://github.com/krazemon/repcoin/blob/master/js/stores/NotificationsStore.js#L59-L62
I chose this solution because it doesn't violate the synchronous property of our stores and allows us to avoid cascading dispatch.

Categories

Resources