How to cache a component once its route is activated - javascript

I have 3 components.
1 parent and 2 child component with router outlet so child component is active whenever its route is called (sharing data using service).
I have complex view for both of this child components.
when i switch between this two routes it takes too much time to render view. ( i have used some bootstrap UI, ngbootstrap, and some more libraries)
my data is not changing between this 2 child components only view part changes which takes time.
so i was wondering is there any way to cache this component once they are called ? to improve performance ?
thanks in advance.

That is a good question, I've never used it, but you can achieve that with RouteReuseStrategy. You didn't provide your specific example, but generally you need to use methods:
shouldAttach - returns true/false to determine whether retrieve method will be called to fetch the component instead of creating it from scratch.
retrieve - returns RouteHandle or null if it's called - it can return saved (cached) component as a DetachedRouteHandle.
shouldDetach - returns true/false to determine whether store method will be called.
store - returns nothing but accepts detachedTree that you can save (cache) to store your component state and further return it from retrieve method.
It sounds complex, but it's easier to do that, than replacing router-outlet to some custom thing. Here is the guy describes that really well and provides an example. Hope that helps.

Related

Where to make API hits ComonentDidMount or in ComonentDidUpdate [React.JS]

In an interview, an interviewer asked me that where should you make API-hits in a simple react application? Meaning in which life-cycle method in a Class-Component. I knew the answer to be ComponentDidMount - because it is the first life-cycle method where we get complete rendered dom meaning dom is now ready!
Then he asked, but why NOT in comonentDidUpdate?
Now, I told him what I had read somewhere, I don't know the exact answer of this -- except ComponentDidMount runs first, so make it there.
Now, can someone tell me if my answer was correct? Or should we make API-hits in ComponentDidUpdate()?
I am confused. Kindly, someone explain with reasoning? Thanks in Advance!
It depends on when you want to call the API:
If an API call is done only once then do componentDidMount
If after render based on some state, you want to fetch data again then do it in componentDidUpdate
EDIT:
Same scenarios can be handled within functional components using useEffect hook as follows:
1- Only runs the first time when the components render same as componentDidMount:
useEffect(() => {
// Run only once when the component renders
}, []); // Pass empty array as dependencies
2- Run every time when component renders either by props change or by local state change same as componentDidUpdate without comparing previous and current props:
useEffect(() => {
// Run every time the component re-renders including the first time
}); // Do NOT pass array dependencies
3- Run only when particular props change, same as componentDidUpdate but with props comparison:
useEffect(() => {
// Run only when the component prop1 and prop2 changes
}, [prop1, prop2]); // Pass props as array dependencies
Reference: Using the Effect Hook
Lets take an example scenario.
You have a profile page and it has a text box which allows you to update tags.
You do a fetch for the whole profile in the componentDidMount to get all the details and show the content.
And then componentDidUpdate will have to be used for something like the update on tags, lets say you do a fetch to get tags based on the user input for every 3 letters the user type. then you use componenDidUpdate to check the state and do the call.
If you think of the same in functional components we'll have to use useEffect.
useEffect(()=>{},[]);
See the array of dependecies, if you pass an empty array it would act similar to componentDidMount.
And the componentDidUpdate
useEffect(()=>{},[tagText]);
Here the effect will run only when a change it done to the tagText, but the componenDidUpdate would be different as you will have to compare the previous state and decide whether the text is updated.
According the Official React Documentation (Link):
componentDidMount
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree). Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
This method is a good place to set up any subscriptions. If you do
that, don’t forget to unsubscribe in componentWillUnmount().
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs.
This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component
has been updated. This is also a good place to do network requests as
long as you compare the current props to previous props (e.g. a
network request may not be necessary if the props have not changed).
Check out this link for a complete big picture. This will be handy while learning react.

Loading configurational data during page load in react app

I'm working on a React application that is connected to a few ASP.NET Core WebAPI microservices. Each of these services have different entities that are used throughout the application.
Within the complete application, there are enums and 'configurational data' that can be configured.
Imagine configurational data as just simple tables, with two fields (Id and Value).
Different entities have FK relationships to the configurational data, and/or have enum fields. I'm trying to understand how I would, in a performant way, can load the configurational data and all the used enums upfront upon page load, so that these can be used in dropdowns. I'm pretty new to React (1 month), so still learning day by day.
I've initially taken the approach of writing a custom DropDown component that accepts a WebAPI GET url, to fetch the possible values for a certain table or enum, but it's very impractical and will prove to be not so performant once there are 1000 users using the application, and all doing calls to these api's multiple times, just for some dropdowns.
So, what is the advised approach to have some sort of splash screen in React AND call APIs to cache values, that then can be used in other components?
"I've initially taken the approach of writing a custom DropDown component that accepts a WebAPI GET url"
You should not do this :)
Before I suggest a solution I want to go through a couple of important key concepts.
Firstly
The render method will always run once before you async stuff happens (like your GET).
Lifecycle methods order which will trigger the First Render : constructor => componentWillMount => render => componentDidMount.
This means that you will have to have all your data ready for render initially. Or have conditions which prevents certain jsx for being called.
Secondly
If you have dynamic content, which will be the options in your dropdown, you'll have to get it from somewhere. If it's static you can define a list locally.
If you want to save the response you could use localStorage or if you are using redux; the middleware redux-persist to persist the store.
I personally don't see the purpose though, because if the dynamic options updates you would want that to update the application state. And even 1000 simple calls like that is not expensive for the server.
If you are using redux, you should keep the options there, because then you won't have to make an GET every time you're mounting the component with the dropdown.
Suggestion:
Many ways you can do this but here is a simple solution).
keep a local state in component and initialize it for first render
this.state = {dropDownOptions: []}
Then in componentDidMount make api call:
fetch(url).then((response)=>this.setState({dropDownOptions: response}));
And lastly in your render method:
<MyDropDown options={this.state.dropDownOptions} .../>

Can componentDidMount share data between pages?

I am still learning React and I apologize if this is a stupid question. I am currently planning about the architecture of my simple App.
I am using Next.js for this project
I have a component that calls the third party API for data every 5 seconds. The data is shared among all pages in the app.
If the component that is fetching the data is not on the main/home page. Are there anyways for homepage to get the data it needs from another page?
For example [Below are all pages]
index.js // Plain simple page that displays current weather and top music
http://localhost:3000/
weatherforecast.js //Using componentDidMount every 5 seconds to fetch Weather Data
http://localhost:3000/weatherforecast
musicplaylist.js //Using componentDidMount every 5 seconds to fetch Weather
http://localhost:3000/musicplaylist
The data shown in homepage has to be refreshed every 5 seconds if there are changes to the following data in weatherforecast.js and musicplaylist.js
I had this in mind but I have a feeling that it's not the right way to do it.
In the Homepage.js, include WeatherForecast and MusicPlaylist components to fetch the data. If this is the case, it seems like I am repeating the same principle in every page.
I found out about Redux which store states as a global object. But how does the state know when to update. But before we getting to state, I am still not sure if components on another page can fetch the data without the user accessing the page.
Thanks for reading this question.
For a pure React solution, you need to utilize other lifecycle methods available for React. componentDidMount is a good spot for making API requests, so you're in the right place to start. However, componentDidMount occurs only once, right after the componentWillMount and after the DOM is ready with a complete render of the component. Setting your API call to run at a set interval will not trigger a rerender of the component, no any of it's sub components. Instead, use componentWillRecieveProps to add your interval request logic. After each interval completes, run setState with the new data from the request to update the default state defined in your constructor. As a bonus step to improve performace, follow up with function that returns a bool in componentShouldUpdate. This way you can strictly define how and when and what is causing any and all component re-rendering.
Redux is an excellent solution to take care of what you want to do. Personally always use it in my React projects to manage state. Using Redux, you could make your API calls still occur in componentDidMount, however the call can hook into your Redux store and update your initial state. The frees you up from have to worry about the local state of your component and how to go about conditionally rendering everything else in your app. Plus Redux abstracts other functions you need, like re-running your fetchToAPI in certain intervals, into their own source (known as Action Creators). Action Creators hook directly in the Redux store so that when one is used, the resulting state diff is passed to Redux's Reducers, which in turn update the application global state. Then all that needs to be done is to have all of your components that need re-rendering on global state change to listen for state changes that occur in the Redux store and conditionally re-render based on the diff of the store from prevState => newProps. This can be setup fairly easy using the boolean check in componentShouldUpdate.
Hope this helps! Cheers.
Redux passes the data throughout the connected components via prop, given that a component is mounted whenever it receives new props a new render cycle is initiated so it will reflect your changes, moreover if you dont want to use Redux you can try using the new context api which is easy to implement and will also be suitable for your solution here is a tutorial on how to use it
To pass data between pages in next.js you will have to use Redux. The idea of Redux is to have a single source of truth. In redux you update the state by calling actions. To update redux state you have to create something called action creators which dispatches action to update the state.
Answer to your question as to if components on another page can fetch data without the user accessing the page is that they do not need to access the data. The components are mounted only when they are accessed, so it will fetch the data when they are mounted.
Regarding the state change notification, refer to below image, and hopefully it is worth one thousand of words:
Original article here.

Flux: should not-top-level views be "dump" (do not fetch data from stores)

Maybe at official flux website I saw a video were mentor said something like:
Only top-level React views should know about stores. All not top level
views should be dump and receive all information as properties.
Question: Is that right? Your argumentation, please
BUT, suppose you have some small React view Button.react that's reused on multiple pages. And suppose Button.react must know about some store data. If we won't fetch all data directly from the Button.react, we get a duplication of code at each top-level component which reuse Button.react. Is that ok for you?
I hope I am understanding your question.
One of the characteristics of React is its one-way data flow. Each component can be used by another component, just like one function can call another function. Just like a function, a React component should typically be able to get all the info it needs to do work (render itself) from the arguments passed into it. This is the function of props in React. When using Flux, sometimes the React Components, which are typically near the top of the view hierarchy, that actually fetch the data from the stores to pass down thru the application are called Controller-Views.
It is not an enforceable rule that every component doesn't become a Controller-View, getting its own state directly from a store, but it is a general practice for good reason. consider the two functions:
function renderToggleButton( isSelected ){
//... render the button
}
vs
function renderToggleButton(){
var isSelected = StateStore.getButtonSelectedState( id );
//... render the button
}
I think you will agree that the second function is more complicated and difficult to test. It has to know from where it is getting it's initial conditions. It also has to know how to identify itself in the context of the application. These are two things the function should not have to know.
Now imagine an application full of functions like this. If one function is misbehaving, it becomes very difficult to trace its inputs; to test it under controlled conditions. I hope that clarifies the guidance given for passing data thru the application as props.

How to do url/view routing with Mobservable, React and possibly React-Router?

I'm looking into mobservable but I'm having some trouble coming up with a good model to do url routing.
Because of how mobservable works with React using a standard react-router does nothing when you change the url. Only if we crudely pass the url path down into the Handler stack to make them reactive do I see some changes.
I feel this needs a different approach. Of course I could hack some custom thing but I kinda like react-routers way of nesting the urls and the solid Location/Link features.
I'm working a (non public) project that uses both mobservable and react-router. That setup is basically as follows:
Create all your routes, but give them all the same handler, your root component.
Introduce reactive state that stores your ui state (like, currently open document for example)
in the router.run callback, use the data that is passed in (the second argument) to update your ui state and to kick off the necessary data retrieval and such. In the end of the callback, just render your handler. Depending on your further setup of the root component you want to pass it the ui state or nothing at all.
For us that setup worked fine so far, so please let me know if you run into any trouble.
EDIT
Another setup using Director can be found in the Mobservable TodoMVC example
Linking this because I found it helpful. I am basically following the approach out here: https://github.com/contacts-mvc/mobx-react-typescript/blob/master/src/components/ContactDetails/index.tsx.
Initialize the store/model in the app's index page or wherever the routes are defined. Then pass the initialized store to the component. Inside the component componentWillMount grab the ID off the route and pass it to a function to load your data.
If you already had data loaded then make a method that changes the selected item. Make sure that select item property is observed and it should automatically update the UI. I think that is what #mweststrate means by UI state.

Categories

Resources