Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knowing when the UI is ready in React / Redux

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:

  1. DOM load.
  2. Initial React render.
  3. HTTP calls triggered from various componentDidMount()s
  4. HTTP calls return. Redux state is updated with HTTP responses.
  5. React renders with new values from HTTP responses.
  6. 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:

  1. 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.
  2. 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.
  3. 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?

like image 433
Nick Heiner Avatar asked Sep 25 '17 20:09

Nick Heiner


People also ask

How do you know when to use Redux?

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.

How does a Redux connected component know when to re render?

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.

Will Redux keep the state when doing hard refresh?

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.

What is dispatch action in Redux?

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.


2 Answers

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.

like image 75
Sotiris Kiritsis Avatar answered Oct 14 '22 03:10

Sotiris Kiritsis


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);
like image 21
pizzarob Avatar answered Oct 14 '22 02:10

pizzarob