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.
Related
I have a page that utilizes the Apollo GraphQL useQuery hook to fetch data from several different sources. Some of that data is displayed on the page in a list, and some gets populated into select menus that the user can change in order to filter data on the page. I'm also using a simple React hook for persisting the users selections via the history.location.state object (specifically using React Router Dom). The hook is essentially this:
history.replace({
...history.location,
state: {
...history.location.state,
[key]: value,
},
});
The issue I'm running into is that whenever history.replace gets called all of the queries re-fetch data, even if nothing has changed and it should serve a cached result. I've been struggling to find any information around when and why the Apollo client decides to re-fetch and if there's anything I can do to avoid this – will Apollo by default respond to any changes to the history object and cause a re-fetch?
I have a server API, that is based on websockets. If the client connects, he must first send a message, and if a success message from server is emitted, client can start working with the server data.
Now i have a angular4 based app, that if the user opens several view components, each component registers to specific events from the API (Using a Websockets service). If the a new state comes from server, the views can update itself. Views can also send update events to server, that recalculates data and sends updates via websockets. The server itself holds a state of the data.
But now, no real client state exists. Without overcomplicating this post (i exclude routing etc), the main target for me now is to integrate redux for managing the state in UI, so that the user can reload the page and see last opened views with a synced with server data.
The current approach of me, is to create create a
Backend service, that holds an WebSocketSubject (RxJS) and manages the sending or recieving from server. I pass an connection observable, that dispatches an event if connection exist. I also have a Subscription inside the service, that dispatches all the websockets push messages as redux events and acts like a middleware
/** this code is a binding to redux word, and acts as middleware push
* #type {Subscription}
*/
this.subscription = this.socket$.subscribe(
(msg) => this.ngRedux.dispatch({type: msg.method, value: msg.parameters}),
(err) => this.ngRedux.dispatch({type: AppActions.CONNECTION_FAILED}),
() => this.ngRedux.dispatch({type: AppActions.CONNECTION_CLOSED})
);
The idea is that i will have reducers, that will react on data state changes from server e.g. if an error is send and a specific view listens to it, it should have a error state.
case BackendStreams.some__error:
return Object.assign({}, lastState, {
error: 'Could not do some calcucations'
});
The server code itself is deterministic and works works the messages from one stack, so i can expect that if i send same 4 messages in same sequence, i will receive same responses.
The problem what i have now, is that if i reload the window (or use the DevTools), the state is recreated, but none of the events are resent to backend. How can i accomplish this? Should i somehow (what tools) tell Redux that some of impurity exists, or have something like a state machine? Im just guessing...
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
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
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.