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

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.

Related

How to share data between components in angular

I use observable to send a value from one component to another (data is sent to the subscriber after clicking on this other component, i.e. via subject) I subscribe in another component and everything works fine until I refresh the page, after refreshing the page the component is recreated and after recreation the subscriber has no data as he did not go through the first component.How can I solve the problem?
I tried using rxjs operators as shareReplay but it didn't work like shareReplay
As your Angular app is destroyed and rebuilt when the page is refreshed, unfortunately you will lose all user state that is not saved somewhere. This is a common problem in building UIs so there are a number of tools available to combat this
Strategy:
Store your user state when an important change is made. This is called persisting state
Fetch and reapply your saved state on reload. This is called hydrating state
Options:
Persist to local storage and check for local storage values on reload to hydrate with
Persist within the users URL (simple values only), e.g. modifying the URL in some way which can be checked on reload. Assuming you are dealing with a single page, query parameters or fragments may be the way to go
Persist to a database via a POST/PATCH call and perform a GET request on reload to check for values to hydrate with
None of these methods are inbuilt into an RxJS operator (as far as I know) but we can easily leverage RxJS to achieve any of the above strategies with little effort. The tap operator is often used specifically to handle side effects, i.e. operations which should happen as a coincidence of an RxJS emission. That is precisely what we want here, in simple terms:
"If the subject emits a value, also trigger an operation which
persists the user state"
"On page load, check for any available saved user state and emit via the
relevant subject, hydrating the observables which the components will consume"
See example implementation below
tab.service.ts
type TabType = 'first' | 'second'
#Injectable({
providedIn: 'root'
})
export class TabService {
tabSelectedSubject: BehaviorSubject<TabType> = new BehaviorSubject<TabType>('first')
tabSelected$: Observable<TabType> =
this.tabSelectedSubject
.pipe(
tap(tab: TabType) => {
// ... your persist code here
this.saveTab()
},
distinctUntilChanged()
)
constructor() {
// ... your hydrate code here
this.fetchAndApplyTab();
}
saveTab(): void {
localStorage.setItem('tab', tab)
}
fetchAndApplyTab(): void {
const savedTab: TabType | null = localStorage.getItem('tab');
if (savedTab) {
this.tabSelectedSubject.next(savedTab)
}
}
}
In this case, we are exploiting the fact that our service is:
A singleton, so only loaded once per app (i.e. provided in the app root)
The service will be instantiated in the first component that loads which also injects it
This allows us to put our fetchAndApplyTab() logic in tab.service.ts's constructor and keep the code self-contained. However, depending on your use case, you may instead want to run fetchAndApplyTab() from your component manually itself.
This is happening because everything is in memory, and on page refresh all is lost, due the fact that angular app is re-initializing. You need to persist the state, for example write it into local storage, for this you could use "tap" operator from rxjs. And also in loading you could read data from localstorage end emit-it, for this you could use app_initializer hook.
there are 2 days majority to pass data between components
If both components are interconnected it means the parent or child
relationships then you can pass data with input-output decorators.
you can use the common service to share data between 2 components.
In SPA application if you refresh the browser then all in memory objects and observables are not present you need to again go back to the screen where it will be initialize.

React+Javascript:-Component will update is getting called too many times causing my application to crash

componentWillUpdate() {
//Axios GET method for getting all the trades
//Adding data of db into mobx store/or syncing data of db and store
axios.get(`http://localhost:8091/trade`)
.then(res => {
this.props.store.arr = res.data;
})
}
This piece of code causing my browser to crash, my laptop to not responding.
Actually, whenever i was trying to delete the row of table containing trade by click of button then the trade was deleted but it took the need of refresh to see that the trade is deleted.
This was because my mobx store and db were not in sync.So as soon i refresh the (REST api) controller updates data in my mobx store.After this i can see that trade is deleted.
So in order to remove the need of refresh i thought to use component will update method.Within that method i tried to sync mobx store with controller data (db data).It worked but it caused the browser to take more than 2.5 gb of memory & at this point all the running applications starts getting crashed also.
So what is the good way to achieve the desired result?
Note i don't know why component will update is getting called too many times.
But i can verify the it because i can see the selection statements(of database) in spring (my server which is sending data to controller ).
Putting the above code inside component did mount is not removing the need of refresh but it is not causing the browser to crash also.
You should not be doing this type of operation in this lifecycle hook. You should use componentDidMount instead for any remote calls that need to happen. However since you are using mobx, you really should not be having these problems as they handle these type of problems for you with the observer pattern. Please read: https://mobx.js.org/getting-started.html to get up to speed and you should have no issues at that point.
Something in your component's props or state is causing it to update often, which is causing a lot of calls to the api. Your best bet would be to find out what is causing those updates and use nextState and nextProps arguments supplied to componentWillUpdate to check and send an api call only when needed. Something like:
componentWillUpdate(nextProps,nextState) {
if (nextProps.needToGetApi !== this.props.needToGetApi) {
//Axios GET here, so that unrelated prop/state change does not cause this to run
}
}
Hint: Add a breakpoint in componentWillUpdate and see what props or state mutations are happening on each call.

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

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

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