I would like to know how long it takes my app to become "ready" for the user to interact with it. The timeline of the app loading includes the following:
DOM load.
Initial React render.
HTTP calls triggered from various componentDidMount()s
HTTP calls return. Redux state is updated with HTTP responses.
React renders with new values from HTTP responses.
All initial loading is complete. The user is ready to interact with the app.
At the end of this process, I'd like to fire a tracking event to record the value of window.performance.now().
I'm not sure what the best way to do this is. React's component system does a good job of decoupling various parts of the UI. In this case, it, unfortunately, means that I'm not going to have one easy place to check to know "has everything been rendered with the new data".
Things I've tried or considered:
Look for a React lifecycle hook that tells me if any child is updating. Something like componentOrAnyChildDidUpdate(). I don't think this exists, and it may be counter to the React philosophy.
Hack together an anyChildDidUpdate() lifecycle hook via context and making every component in my app either subclass a helper abstract base class or be wrapped in a higher-order component. This seems bad because context is an experimental API.
Subscribe to the Redux state via store.subscribe(). Update the Redux state to have an explicit record of whether all the HTTP calls have returned. Once all the HTTP calls have returned, and React is finished re-rendering, then I know to fire the tracking callback. The problem is knowing when React is finished re-rendering. It would work if my store.subscribe() the callback was guaranteed to be executed synchronously after react-redux's callback. But that is not the case.
Is there a good way to do this in React?
I'm afraid there is not a universally good way to do this in React, this is "logic" that is related with the structure of your application.
I wanted to display a loader when navigating from one page to another in Single Page Application(SPA) written in react and I faced a similar problem not knowing when to stop displaying it and if all the components in the new page have completed their API calls/renders.
The way I resolved it was:
1) I removed all my API calls from inside my components.
2) Create a Page component to wrap all the components included in that page(invoked by your react router).
3) Perform all requests required for your components simultaneously on navigation(in my case while displaying the loader). For each request that completes, create a promise and inside it create your component dynamically using React.createElement. Pass to the component created the request response and the promise handler as props.
4) Resolve the promise in your component's componentDidMount.
5) Once all the promises are resolved you know the "page" is ready and you can record the value of window.performance.now().
It is hard to provide a minimal code example without a lot of out of context code since this code is spread across the application.
There's more than one way to do what you're asking - but off the top of my head I would do the following:
• Create a perf reducer and call performance.now() right before I make the redux store and add the value to the initial state.
{ perf: { start: '...', end: '...' } }
• Track loading status of initial HTTP requests in a loaded reducer.
{ loaded: { request1: false, request2: false, request3: false } }
• Connect top level component to loaded reducer and check if all requests are complete in componentDidUpdate. If true add end value to perf reducer.
import React from 'react';
import { connect } from 'react-redux';
class App extends React.Component {
componentDidUpdate(prevProps) {
if (!prevProps.loadingComplete && this.props.loadingComplete) {
this.props.updatePerf(performance.now());
}
}
}
const mapStateToProps = state => ({
loadingComplete: state.loaded.every(item => item),
});
const mapDispatchToProps = dispatch => ({
updatePerf(time) {
dispatch({ type: 'SET_ENDING_PERF', payload: time });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
In our project, we use redux and a couple of actions - trigger, request, success, error.
"Trigger" calls request and throw loading: true into our "smart" component
"Request" fetch data and throw them into Success/Error
"Success", tells us that everything is fine and data loaded and throw { loading: false, data }
On success/error, we always know that is happening with our application
And of course you can use componentWillReceiveProps(nextProps) {} and check loading flag and data in your component
You can try to use react-addons-perf
That's how I benchmark my apps usually:
Basically you want to start measuring when your component starts updating and stop the measurement after the lifecycle method componentDidUpdate().
import Perf from 'react-addons-perf'
componentWillMount() {
window.performance.mark('My app');
}
componentDidMount() {
console.log(window.performance.now('My app'));
Perf.start()
// ... do your HTTP requests
}
componentDidUpdate() {
Perf.stop();
Perf.printInclusive();
}
Like that you will track your component initial render + your component updates.
Hope it helps
Two steps to achieve this.
Track all HTTP-requests via actions
Simple counter in redux state would be enough, that increments on start of request and decrements on success or fail (all via appropriate actions).
No window.dirty.stuff here in front of Dan Abramov.
Track all connected components
componentDidUpdate method indicates that all children finished rendering. This can be achieved without much boilerplate with recompose's lifecycle HOC. Right place to start tracking re-render might be sCU method or just cWRP if you use recomposes shouldUpdate or reselect already - since we won't track components that didn't update.
Note that all is assuming idiomatic react/redux app, no tricky side-effects or animations. For example, if some child component somewhere deep in the hierarchy fires its own AJAX request, say on cDM, not via redux action. This way you have to track them too, BUT no one can be sure if they imply any re-renders.
Tracking such behaviors is still possible, just might require more effort.
from a design POV, in my opinion being ready or not is a user presentation property and so should be handled by react components alone.
for example, you may later decide to allow the user to interact with some part of your UI while some other parts are still loading, or you may decide that some components should allow the user to interact before the data model is ready, etc ... in other words, it's a (react) component responsability to tell if/when it's ready or not and nobody else.
That is, all 'waitable' components could expose an onLoad-like prop to be passed up the react tree; the root component would then coordinate the result by changing the tree down stream accordingly by propagating the appropriate props down to leaves.
This could be implemented by writing a mixin/high order component to incapsulate the "readyness update" logic.
Related
I have always made API's request in ComponentDidMount lifecycle and called setState in the callback. But while digging deep I happened to find out We should not call setState in this lifecycle as it triggers render method again and React also doesn't recommend that.
Then in that case where exactly we should make that request and call setState to use result after that?
You should absolutely make an API call in componentDidMount. What is not recommended is to re-render a big component, instead what you should do is break your UI into small logical components and only re-render only what is needed , not a whole. For example, you have a big component named HomeComponent which has 3 small components called NavComponent, BodyComponent and FooterComponent. You should NOT be calling the API from the componentDidMount of the HomeComponent since calling setState from HomeComponent will re-render all the small components inside HomeComponent, which is not necessary since you know that the navbar or footer is not needed to be re-rendered. Rather from the BodyComponent, because only the body part needs to be re-rendered since its state has changed. So you should be calling the API from the componentDidMount of the BodyComponent, that way you re-render only whats needed.
#Shababb Karim is right but if i were to implement API calls in my project i would add a state management library. Redux is a great way to implement a global state to the app where you can set data from said API. All components that need that data can be attached to the Redux state to get it.
Redux can be a bit of overhead in smaller projects though. In this case there are other options like Flux (although i like Redux more).
Redux allows using complex container components inside other "dumb"/presentation components. Point is that mostly the simple components are const literal functions, so with stateless mod it will re-render...a lot.
My concern is that it will also call the components it contains on each render, including these containers that can trigger API calling actions eg. inside componentDidMount or other life cycle method.
My current solution is a 'cache' inside store, I trigger api method, but inside I check, should the request be made*/is cache invalidated to continue or break the API call.
Problem is that these components in my opinion should not be tight coupled, so I cannot trigger the load after some other actions inside totally different component, cause that way it won't be independent.
* - due the fact that store in this case is just a simple map, I check whether the data already exists(or timestamp is outdated) or is it just missing.
Just to picture it better:
<SimpleView>
<Container/>
</SimpleView>
(...)
class Container extends React.Component {
(...)
componentDidMount() {
apiCallingActionTrigger()
}
It is my understanding that you are not supposed to change states in the render function cause that would cause and infinite re render or the component.
This makes perfect sense, but I find myself in a particular situation. I am building an offline application and I am using an offline storage system to retrieve data. Now, whenever a method is called to get certain data, cache is checked, if it is not expired the component will be able to access the data and therefore nothing happens, but if it is expired there is a call to the API, the data is updated and the interested components re-rendered.
These methods will change the state of the component the first time they are called because they are going to the API grabbing the new data and re-rendering, and then they will not change the state anymore because the data will already be in cache.
Now, I could call these methods in component will mount, and that is what I am doing now, but if I am forced to re call them, I need to unmount and remount the components. Is this the only possible way of doing this?
Thanks!
Well the first step is understanding that state management and rendering needs to be decoupled which you got already.
Now what you can do is consider your external state/cache element as an observable object (ie. I want to do something like observableObject.listen('change', onChangeHandler); you can use EventsEmitter from the events library). You do that listening on componentDidMount and clean up in componentWillUnmout. That onChangeHandler is very simple: this.setState({ value: observableObject.value }) which will trigger a component re-render which should be a pure function that outputs DOM nodes depending on props being passed and it's own state.
This means that your logic of checking if the cache is invalid is not on a per request of the value (inside rendering) but rather think of the object as self contained. It regularly checks if itself needs to notify its listeners that it changed. Since JS does not do parallel execution you don't have to deal with threads and synchronization. You know that at the point in time your render function executes it will have the latest value. If after rendering the logic that checks for cache executes and it sees that it needs to be updated it simply notifies as said earlier its listeners and that makes your component re-render because your onChangeHandler changed the state.
Hope I helped.
I have a react component, In which I am using a date picker. Based on value of Date selected I am sending an ajax request to fetch data.
I am not using any frameworks like redux or flux.
export default class MyComponent extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// Initial fetch request based on the default date
}
onDateSelectionChanged(fromDate, toDate) {
this.setState({
fromDate,
toDate
});
}
render() {
return (
<div className="row">
<DateRangePicker callBackParent = {this.onDateSelectionChanged}/>
{/* other stuff */}
</div>
);
}
}
Now suppose I changed the date to another date. What is the best way to fetch the data again? should I fire request again inonDateSelectionChanged or is there any life-cycle method?
I strongly suggest to decouple the ajax logic from your component. Remeber that plain React was build only to simplify the rendering of the views and not for complex logic like Http calls.
Using Flux you can quickly create the infrastructure for handling both the UI rendering and any other logic for your app.
The complete tutorial is here but I will add a quick summary so that you could easily get started.
Add 4 classes that will be your infrastructure:
YourComponentActions - this class will handle the "actions" that your component will fire. An action is actually an event that will be fired from your component to something that will perform the actual logic (point 4).
SharedConstans - this class will hold the event names of your app.
AppDispatcher - this class will manage the event handling of your
app.
YourComponentStore - this class will register to the action's event and handle the http call. Here is the logic of your component that is decoupled from the UI. After you receive a response from your ajax call you will fire another event from your store and your component will register to it and only then update the state.
It feels complex however from now on you will easily add any logic to your app while keeping it decoupled, readable and easy to maintain.
Read more about Flux here.
You should fire another network request in onDateSelectionChanged, there's no lifecycle method for when state changes.
Technically speaking you could do some logic in componentWillUpdate (or worse, shouldComponentUpdate) to make a request when that state field changes and it would work, but you shouldn't. Both of those lifecycle methods have well defined purposes, making network requests would make your code less clear and harder to maintain.
If you really insist on sending the request from your component method, than firing it in onDateSelectionChanged is definitely the way to go. As it responds to every Date change, it is naturally the only method capable of fulfilling your needs, and the lifecycle methods are not directly aware of the Date change nor the right place to do it. Implementing something like that in e.g. componentWillUpdate or componentDidUpdate can possibly lead to cyclic execution and that isn't something you want to face without a good reason.
Speaking of lifecycle methods, the only one explicitly recommended to fire requests is componentDidMount method, where you have a good opportunity to do some ajax initialization operations, as you can see in docs, but none of them is suitable for ordinary data fetching.
On the other hand, I suggest you really have a look at Flux, which is an architecture solving many issues, separation of concerns is on of them. Your problem here is that you tie your component to creation of Ajax requests, which doesn't promote reusability and makes your code harder to maintain. Think of components as tools to present content and capture users inputs, bothering with requests and response or processing and storing incoming data shouldn't be their concern (at least at larger scale).
You can of course separate your request creators to external functions, but if you tend to write React frontend, you will sooner or later face problems like handing props over many intermediary components or propagating events up through your component hierarchy, which is very tedious and messy without some kind of architecture, and Flux is the best solution for these issues and therefore number one technology to learn - if you mean it with React seriously.
onDateSelectionChanged(fromDate, toDate) {
var self = this; // we can not use this inside ajax success callback function
this.setState({
fromDate,
toDate
}, function() { // callback fires after state set
// your ajax request goes here, let say we use jquery
$.ajax({
url: fetch_url, success: function(data) {
// again we can setState after success, but be careful
// we can not use "this" here instead we use "self"
self.setState({
someState: data.someValue
})
}
})
});
}
Having spent some time working with flux (both ‘vanilla' and with various frameworks including alt and fluxible) I am left with a question about best practice with regard to loading the initial state of components. More specifically about components directly accessing the store to do it.
The flux ‘model’ prescribes a unidirectional flow of data from Action>Dispatcher>Store>View in a loop, yet it seems this convention is eschewed when loading the initial state of components, most docs/tutorials contain examples where rather than firing an action to get the data, the component calls a function on the store directly (examples below).
It seems to me that components should have little/no information about the store, only about the actions that they can fire, so introducing this link seems both unintuitive and potentially dangerous as it may encourage future developers to jump straight to the store from the component instead of going via the dispatcher. It also runs counter to the ‘Law of Demeter’ which Flux is supposed to adhere very strongly to.
What is best practice for this? Is there a reason that this always seems to be the case? Its quite possible that I have missed out something fundamental, so please let me know if so!
Thanks.
Examples of components calling the store directly.
Flux React example from the fb flux repo example chat app (https://github.com/facebook/flux/tree/master/examples/flux-chat)
MessageSection.react.js
getInitialState: function() {
return getStateFromStores();
},
function getStateFromStores() {
return {
messages: MessageStore.getAllForCurrentThread(),
thread: ThreadStore.getCurrent()
};
}
Another example from the same repo for the TODOapp
(https://github.com/facebook/flux/tree/master/examples/flux-todomvc)
TodoApp.react.js
function getTodoState() {
return {
allTodos: TodoStore.getAll(),
areAllComplete: TodoStore.areAllComplete()
};
}
Example of the alt implementation of the above todo app: (https://github.com/goatslacker/alt/tree/master/examples/todomvc)
TodoApp.js
function getTodoState() {
return {
allTodos: TodoStore.getState().todos,
areAllComplete: TodoStore.areAllComplete()
};
}
and finally the alt specific tutorial:
(https://github.com/goatslacker/alt-tutorial/blob/master/src/components/Locations.jsx)
Locations.js
componentDidMount() {
LocationStore.fetchLocations();
},
It depends on how the structure of you app looks like. Often you want to fetch some data before showing something to the user. What I have found to be a good practice is to have a high end component which fires on mount an action which fetches any initial data from an API. This means that when this action is done fetching it calls the store which caches the data and then emits a change.
This change emit then sets in motion the re-rendering of the whole application.
This way you keep the uni-directional data flow. The whole point with Flux is letting the user extract the data flow functionality out of components to keep them more clean, discourage components to directly communicate with each other and decrease the amount of unnecessary properties which has to be passed around the application.
In the examples the initial state fetches some initial value from the store. This is a common way to fetch the initial value, but you could set it in the component as well. Both ways I would say is a good practice. This does not mean that every component is free to fetch whatever they like from the store.
Anyway, the goal would be to keep the code and data flow as intuitive as possible with Flux. All of this are reasons why there are so many implementations of Flux.