How to share data between components in angular - javascript

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.

Related

Run a component function every time its rendered

I've got an Ionic Angular app that calls an API every time the view changes and stores the value in ionic storage.
Every page has a 'header' component that is supposed to display this value from storage.
Inside ngOnInit(){} I fetch the value from storage and the value updates. However, it only does it once per page view. So if I view the homepage, for example, go to page 2, and back to the homepage, the code won't re-execute.
I imagine this is because the page doesn't actually unmount when you change the view, and therefore has no reason to 're-construct'.
constructor(){} only runs on the first construct for that view
ngOnInit() {} only runs the first init for that view
ionViewDidLoad() {} doesn't run because its not a view and that's an ionic page life cycle hook
I'm basically looking for a way to call an API, store the value in ionic storage, and on every page change, update the value in the header.
I've been thinking this might be achievable with Observables, but I'm not sure how this would work with ionic storage.
Or even get the value from storage in the view using one of the ionic view lifecycle hooks and pass it into the header as a prop, but that just seems messy.
The reason I'm using storage is that the balance gets used elsewhere and not just in the header and it saves me making the call multiple times if it's just available in storage.
Any recommendations appreciated
export class HeaderComponent {
balance = 0.00;
constructor(
private storage: Storage,
) { }
async ngOnInit() {
this.storage.get('balance').then(data => {
this.balance = data;
});
}
}
If you're positive that ngOninit in HeaderComponent is only firing once, then that means that it's not being destroyed between route changes. That would make sense if your component is present in all of your views.
I would not use local storage to distribute your application state to your app components. If your app state is relatively simple, then observables are the way to go. Create a service that holds the state of your application in an observable. Every time you fetch data from the API, push the new data to the observable. Then in your HeaderComponent subscribe to the observable in the ngOnInit hook so that your component data can be automatically synced to the application state any time there is a change.

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.

Redux State Resets On Window Reload (Client Side)

I have very large and complicated objects like userInfo, chatInfo, and etc as in objects & arrays with very large and nested information. The thing is in my react app every time I refresh my page the redux state gets reset and I have to call all those API's again.
I did some research on this topic. I checked Dan Abramov's egghead tutorial on redux. What he does is maintain the redux state in localStorage of the browser and updated the localStorage after every 100 or 500 ms. I feel as if this is a code smell.
Continuously watching the localStorage state and updating it, wouldn't it effect the performance of the browser. I mean wasn't this on of the reasons Angular 1 failed because it continuously kept on watching state variables and after a while if the site was kept live in the browser it just slowed down. Because our script continuously kept on checking the state of the variables. i feel as if we are doing the same thing here.
If maintaining the redux state in localStorage is the right approach can someone tell me why so? And if not is there a better approach?
This is not a duplicate of How can I persist redux state tree on refresh? because I am asking for advice whether persisting state in local storage is a code smell or not
I think using localStorage is your best option here, since it seems the data you are storing there is needed on the client. If the data is not changing, you shouldn't need to repeatedly query, or watch, the localStorage.
Another thing you can do is wrap a closure around your localStorage, so that you are not always hitting disk when retrieving your "large" data. Every browser implements localStorage differently, so there are no guarantees on consistent behaviour or I/O performance.
This also adds a simple layer of abstraction which hides the implementation, and controls everything related to your user data in one place.
Here is a simple example of user profile data closure:
// UserProfile.js
var UserProfile = (function() {
var userData = {};
var getUserData = function() {
if (!userData) {
userData = JSON.parse(localStorage.getItem("userData"));
}
return userData;
};
var setUserData = function(userData) {
localStorage.setItem("userData", JSON.stringify(userData));
userData = JSON.parse(localStorage.getItem("userData"));
};
return {
getUserData: getUserData,
setUserData: setUserData
}
})();
export default UserProfile;
Set the user data object: This will overwrite the localStorage, and set the local variable inside the closure.
import UserProfile from '/UserProfile';
UserProfile.setUserData(newUserData);
Get the user data object: This will get the data from the local variable inside the closure, or else go get it from localStorage if it is not set.
import UserProfile from '/UserProfile';
var userData = UserProfile.getUserData();
The idea here is to load data into memory, from localStorage, the first time, when your app loads, or on the first API call. Until such a time when the user profile data changes, (i.e. a user updates their profile, for example), then you would query the API again, and update the data again via the UserProfile.setUserData(..) call.
The question is at what point you need to achieve persistent. I feel that the answer in your case is on a page reload. So if you are worry about performance I'll say:
* Update the localStorage only when the state changes. Inside your reducer when you update the state.
* Read from localStorage when you boot the app.
(This way you write only when the state changes and you read only once)
P.S.
I'll recommend https://github.com/rt2zz/redux-persist package for achieving persistent in Redux apps.
I would only do this as a weak cache and would not rely on it. Local storage is limited (5mb on Chrome e.g.), and may not be available. You'd have to carefully verify that your data was written.
As others have said, you wouldn't be watching localStorage you'd be periodically flushing the store. But I would agree that it seems like a rather coarse hack to blindly assume that all of your state was appropriate to persist in local storage. As with all caching solutions, you need to carefully consider the implications (freshness, expiration, etc.). It sounds like you might want to do this incrementally - pick off a few low-hanging pieces of fruit that are appropriate for caching and consider the implications of caching that state in local storage.
Try redux-persist you can do optimistic persistence, agnostic of the underlying platform (web/mobile).
If you still find performance is a bottleneck. You can do either of the following in steps.
Cache Middleware
Create a middleware that listen's in on changes and records them but only flushes them out every 5 seconds.
Attach an event handler to window.beforeunload to detect user navigating away or closing the browser to flush changes
Persistence Strategy
To persist the data you can use a either of the two strategies below.
Store it in localStorage if there is no performance bottle neck.
Send a JSON blob to server like file-upload. Fetch last JSON state when app loads and persist locally.
I'd suggest you start of with using redux-persist. If there is still a performance bottle neck then use a cache middleware along with one of the two persistence strategies.
I think that in most cases you want to persist your state in localStorage after certain action(s) happens. At least that's always been the case in the projects where I've needed to persist it.
So, if you are using redux-saga, redux-observable or redux-cycles for orchestrating your side-effects you can easily make that side-effect (persisting the state into localStorage) happen whenever one of those actions take place. I think that's a much better approach than "randomly" persisting the data based on a time-interval.
It seems that your understanding of watching state is that you have some kind interval which will keep checking your state and updating it in localStorage. However, I think you can achieve this same thing by updating your localStorage in the react lifecycle method componentDidUpdate. This method will fire every time your component updates, so you can take advantage of it and update your localStorage every time it fires without incurring any performance hits.
One option is to load the data in INITIAL_STATE
window.__INITIAL_STATE__ = { ...state... }
and load the reducer:
window.__APP_STATE__.__REDUCERS__.push(reducer)
It is completely ok to persist state in large redux+react applications.
Regarding angular1, watchers and digest cycle though on every state change even if you rehydrate state not all components are rendered because of redux connect API and vrtualDOM of react.
You can check:
https://github.com/redux-offline/redux-offline
https://github.com/rt2zz/redux-persist
Performance should not be your primary concern. If it comes to that normalise your state and only persist important information like drafts etc(Less info-fewer rehydrations).
The bigger problem with this kind of setup is usually if you have any background sync or socket updates in the app. Having multiple tabs of browser causes async writes to local DB causing to overwrite with previous states.
Here is a thin wrapper on top of it for cross tab sync redux-persist-crosstab
You can check some of these implementations in mattermost webapp and how they use it for realtime app.
They go through some hoops for extra stability and performance - link to store file in mattermost webapp
I think you can use npm module called redux-storage. It provides Persistence layer for redux with flexible backends. You can combine redux-storage with any redux-storage-engine you want. And in my view you can combine this with redux-storage-engine-sessionstorage assuming you will only want to share and save information till browser is open. No need to bloat browser with localStorage. You will need extra javascript support for this to get it clear.
Redux-storage will trigger load and save actions after every state changes. Providing all more flexibility about what to do in case of load and save. Also if you don't to save some state changes then you define array of state which you want to filter out.

Should I use redux dispatch during initial rendering?

I'm trying to build a E-Commerce Store and it requires that I initially load a list of trending products on the home page.
Here, I can simply do without redux and simply display the data (roughly) of this sort
const trendingProducts = await get('/api/trendingProducts')
render(){
<TrendingProducts data={this.trendingProducts.data} />
}
I am using redux in my application. So should I do a dispatch elsewhere to fetch the trending products ?
All in all, Should I always handle every single fetch / render using only Redux dispatch ?
Redux is a store management for your application's state. Redux's dispatch is used to dispatching actions that aims to update your application's state in some way.
Hence if your application logic requires displaying information that belongs to your application's state - you need to take it from Redux store. If such information is not yet available into Redux store - you need to obtain it from some source (e.g. fetch) and use dispatch to update your application's state. If information, you're trying to display is not part of your application's state - you can display it directly, but in this case you'll need to handle React lifecycle events by yourself too since React re-draws components upon component's state change.
UPDATE: Your example code will work fine if you'll put your trendingProducts into component's state:
class MyComponent {
constructor() {
super();
this.state = {
trendingProducts: {}
}
}
componentWillMount() {
fetch('/api/trendingProducts').then(data => this.setState({trendingProducts: data}));
}
render() {
return (
<TrendingProducts data={this.state.trendingProducts}/>
)
}
}
That is very subjective and there is no correct answer but I can tell you by my experience.
Always keep your business logic separate from your component
If you're making API calls then you should definitely dispatch an action rather writing this in your component because there are lot of redux-like stores are emerging and it might happen that later you want to change your store using some different architecture. Your view layer should be independent on your API calls (business logic). In this way, while refactoring your app again, you'll just have to change the logic and your view will remain the same.
I'm writing this on my experience where we started refactoring the whole app from backbone to React. We had models and collections, we changed the whole html but we didn't change any business logic initially, just the views were deprecated but later we removed the business logic too using redux (iteratively). You should write your code in such a way that it has maximum reusability after all that's what React is and that's how you should write your front-end code.
Also, the component's state can reside in component where the whole app doesn't get affected. e.g. showing or hiding of a third-pane or overlay etc.

Angular - initiate a response when data loads/changes

Relative Angular newbie here, and I am wrestling with what would seem like something most applications need:
Watching a model/data and doing something when that model is hydrated and/or has a state change.
Use case would be, when a user logs in (user model gets initiated) a complimentary directive/controller sees the state change, and then requests out to the backend to get a list of this users corresponding data elements (ie Notifications, emails, friends, etc)
Ive parsed through StackOverflow and such, and it always appears that a shared service is the way to go, however I never find a definitive answer about how the directives are to watch the state change. Some suggest a broadcast/watch while others say that is a bad pattern.
Our app currently does employ a shared UserService, which contains model representation of a User (data and simple methods is fullName())
This service also has a subscription hook that directives can subscribe to
onLogin: (fn) ->
$rootScope.$on userService::login, fn
and the use is:
UserService.onLoad(myFunction)
When the UserService loads the User, it then broadcasts userService::login and all the listeners are run. Hence everyone that shares the UserService can subscribe and respond to a User logging in.
This all works. But I was thinking there must be a built in Angular way that the directives can just know about the state change and then do myFunction (ie make additional data calls)
Thoughts and feeling would be extremely appreciated!

Categories

Resources