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:
componentDidMount()
sAt 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:
componentOrAnyChildDidUpdate()
. I don't think this exists, and it may be counter to the React philosophy.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.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?
Redux is most useful in cases when: The app state is updated frequently. The logic to update that state may be complex. The app has a medium or large-sized codebase, and might be worked on by many people. You need to see how that state is being updated over time.
It deduces the changes of your data by running the reducer function you provide, and returns the next state that corresponds to every action dispatched. React Redux then optimizes component rendering and makes sure that each component re-renders only when the data it needs change.
When we refresh page in a web-app, the state always resets back to the initial values which in not a good thing when you try to build some large web-app like e-commerce. We can manually do the state persistent using the native JavaScript localStorage.
dispatch is a function of the Redux store. You call store. dispatch to dispatch an action. This is the only way to trigger a state change. With React Redux, your components never access the store directly - connect does it for you.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With