Reactjs async rendering of components Reactjs async rendering of components ajax ajax

Reactjs async rendering of components


There are two ways to handle this, and which you choose depends on which component should own the data and the loading state.

  1. Move the Ajax request into the parent and conditionally render the component:

    var Parent = React.createClass({  getInitialState: function() {    return { data: null };  },  componentDidMount: function() {    $.get('http://foobar.io/api/v1/listings/categories/').done(function(data) {      this.setState({data: data});    }.bind(this));  },  render: function() {    if (this.state.data) {      return <CategoriesSetup data={this.state.data} />;    }    return <div>Loading...</div>;  }});
  2. Keep the Ajax request in the component and render something else conditionally while it's loading:

    var CategoriesSetup = React.createClass({  getInitialState: function() {    return { data: null };  },  componentDidMount: function() {    $.get('http://foobar.io/api/v1/listings/categories/').done(function(data) {      this.setState({data: data});    }.bind(this));  },  render: function() {    if (this.state.data) {      return <Input type="select">{this.state.data.map(this.renderRow)}</Input>;    }    return <div>Loading...</div>;  },  renderRow: function(row) {    return <OptionRow obj={row} />;  }});


The basic example of async rendering of components is below:

import React                from 'react';import ReactDOM             from 'react-dom';        import PropTypes            from 'prop-types';export default class YourComponent extends React.PureComponent {    constructor(props){        super(props);        this.state = {            data: null        }           }    componentDidMount(){        const data = {                optPost: 'userToStat01',                message: 'We make a research of fetch'            };        const endpoint = 'http://example.com/api/phpGetPost.php';               const setState = this.setState.bind(this);              fetch(endpoint, {            method: 'POST',            body: JSON.stringify(data)        })        .then((resp) => resp.json())        .then(function(response) {            setState({data: response.message});        });    }    render(){        return (<div>            {this.state.data === null ?                 <div>Loading</div>            :                <div>{this.state.data}</div>            }        </div>);    }}


Async state management (Playground)

The following solution allows for async state management and can be used for HTTP related requirements if implemented correctly.

Requirements

  • Only re-render elements consuming the observable.
  • Automatically subscribe and unsubscribe from the observable.
  • Support multiple and joint observables.
  • Provide a loading state
  • Simple and easy implementation

Expected behaviour

return (    <Async select={[names$]}>        {result => <div>{result}</div>}    </Async>);

The provided example above will subscribe to the observable names$. The content/children of the Async component will re-render when next is fired on the observable, not causing the current component to re-render.

Async Component

export type AsyncProps<T extends any[]> = { select: { [K in keyof T]: Observable<T[K]> }, children: (result?: any[]) => JSX.Element };export type AsyncState = { result?: any[] };export default class Async<T extends any[]> extends Component<AsyncProps<T>, AsyncState> {    private subscription!: Subscription;    constructor(props: AsyncProps<T>) {        super(props);        this.state = {};    }    componentDidMount() {        this.subscription = combineLatest(this.props.select)            .subscribe(result => this.setState({ result: result as T }))    }    componentWillUnmount() {        this.subscription.unsubscribe();    }    render() {        return (            <Fragment>                {this.props.children(this.state.result)}            </Fragment>        );    }}