简体   繁体   中英

React router 4: How to wait for a promise to resolve, inside render method of Route?

I'm trying to achieve functionality, where each Route will first wait for some ajax promise to resolve, and only then will render the route. I saw that onEnter doesn't exist anymore, so i'm trying the render method.

My routes are defined like that:

{cmsRoutes.map((route, idx) => {
              console.log(route.resolve)
              return route.component ? (<Route  key={idx} path={route.path} exact={route.exact} name={route.name} render={props =>{
               route.resolve()
               .then(({data})=>{
                 console.log(data)
                  return (
                <route.component {...props} />
              )
                }) 

              } } />)
                : (null);
            },
            )}

As you can see, it just iterates over some array, that holds the data for each route. One of the fields of the route object is "resolve", which points to a function, that returns a promise. Like this one:

const resolvemanageContactApplications = ()=> {
  return ajax('/contact')
};

When executing this, i get the following error:

Route(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

This of course happens, because while the ajax is being executed, nothing is returned from the route. The question: how can i make React router 4 wait for the promise resolution? There has to be some way. I remember that AngaulrJS UI-Router actually had some "resolve" api.

You need another approach here.

You should use a state to store if your ajax is done or not. Then, to render the route you can render nothing (null or ...loading) if the ajax isn't done, and render the actual component or whatever you want with the result of the ajax.

I can see you are generating the routes based on an array of data, and to achieve that I would create a Component to wrap the data-loading condition. It could receive as props:

  • The resolve (the ajax function)
  • The component to render

On the componentDidMount and componentDidUpdate do the ajax call and set the state.waitingResolve to true, and on the .then() of the resolve, set the state.waitingResolve to false, and store the returned data to the state too.

On the render, you check if state.waitingResolve , and renders null (or ...loading) or the component to render that you received from props.

It may look like complicated but it is worth.

You can create some async component wrapper like:

class AsyncComponent extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      Component: null
    };
  }

  componentDidMount() {
    const { componentPromise } = this.props;
    componentPromise.then(component => this.setState({
      Component: component
    }));
  }

  render() {
    const { Component } = this.state;

    if (!Component) {
      return null; // You can return some spinner here
    }

    return (
      <Component {...this.props} />
    );
  }
}

And use it inside of your router:

<Route
  key={idx}
  path={route.path}
  exact={route.exact}
  name={route.name}
  render={props => (
    <AsyncComponent componentPromise={route.resolve} {...props} />
  )}
/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM