React - Asynchronous Component Rendering Wrapper
Tags: react, javascript, frontend, webdev
Published: 2018-05-09 16:16:23

---
title: React - Asynchronous Component Rendering Wrapper
published: true
description: Improving the user experience and code by rendering components asynchronously
tags: react,javascript,frontend,webdev
---

Most of the time our front-end applications interact with a wide range of services and APIs for populating and displaying the necessary data. We usually display loading screens for the same and we make the user wait a certain amount of time before we actually allow them to use the page. But sometimes most of the necessary content for the user is available but the user is made to wait for the unnecessary data on the page to load. This is very bad when it comes to user experience perspective.

Consider this scenario, You are opening a blog link. The text loads much faster but then the page doesn't allow you to navigate until the pictures and side links are loaded. Instead the page can allow you to navigate while the pictures and other things load simultaneously.

One of the ways of tackling this issue in react is to use an asynchronous wrapper for rendering the component. Let's take two components `HeadingComponent` and `ParagraphComponent`.

```jsx
const HeadingComponent = props => <h1>{props.data}</h1>;

const ParagaphComponent = props => <p>{props.data}</p>;
```

We will now create the `AsyncComponent` that acts a wrapper for the `HeadingComponent` and `ParagraphComponent` which displays data from two different APIs.

```jsx
class AsyncComponent extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      resolvedError: false,
      resolvedSuccess: false,
      data: '',
      error: '',
    };
    this.renderChildren = this.renderChildren.bind(this);
  }

  componentDidMount() {
    this.props.promise()
      .then(data => this.setState({ resolvedSuccess: true, data }))
      .catch(error => this.setState({ resolvedError: true, error }));
  }

  renderChildren() {
    return React.Children.map(this.props.children, child => (
      React.cloneElement(child, {
        data: this.state.data,
      })
    ))
  }

  render() {
    if (this.state.resolvedError) {
      return <h1>Error Encountered</h1>;
    } else if (this.state.resolvedSuccess) {
      return <div>{ this.renderChildren() }</div>;
    } else {
      return <h1>Loading...</h1>;
    }
  }
}
```

The `AsyncComponent` takes a prop called `promise` that it calls from `componentDidMount`. If it resolves successfully it stores the data in the state and error in case of a rejection. Then in the render the method we render
1. Error component incase of error
2. The child nodes if resolves successfully
3. Loading component otherwise

Sometimes the child components needs the response data. React doesn't allow us to get the component directly from the child elements so we use React's inbuilt functions like `React.Children.map` and `React.cloneElement`. We traverse the children of the component and we clone each child element by adding a prop `data` which has the actual response from the API so that the response is accessible to children as well.

Final piece of code that puts all of the above together

```jsx
const HeadingAPI = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve('Heading'), 5000);
});

const ParagraphAPI = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve('Paragraph data'), 2000);
});

const App = () => (
  <div>
    <AsyncComponent promise={HeadingAPI}>
      <HeadingComponent />
    </AsyncComponent>
    <AsyncComponent promise={ParagraphAPI}>
      <ParagaphComponent />
    </AsyncComponent>
  </div>
);
```

Here's a Codepen running the scenario with both the promises resolving successfully.
{% codepen https://codepen.io/sadarshannaiynar/pen/PeQRQO %}

Here's a Codepen running the scenario when one of the promises is rejected.
{% codepen https://codepen.io/sadarshannaiynar/pen/aGqYRv %}

As you can see the failure of one API doesn't affect the rendering of the other component and the user can continue navigating the webpage regardless. This greatly improves the user experience and also reduces the amount of redundant code created by API calls across components.

You can still improve the wrapper by giving custom loader and error components to make it look more fancy.