react-redux-starter-kit icon indicating copy to clipboard operation
react-redux-starter-kit copied to clipboard

Put rendering on-hold until initial fetch is done

Open rpribadi-ds opened this issue 8 years ago • 5 comments

Hi @davezuko , I have a question on how to postpone child-rendering until a certain condition is met.

For example: Let's say I have an "app" component. And on top of that, I have 2 modules inside my route: /products -> accessing Product module. /users -> accessing User module.

The app will render the children component based on its route. but, on "app" component, there's a necessary API that should be called first before rendering its children component.

App/Component.js

class CoreLayout extends React.Component {
  componentWillMount() {
    this.props.fetchInitialSettings();
  }

  render() {
    return (
      <div>
        <Header />
        {this.props.children}
        <Footer />
      </div>
    );
  }
}

If the user access '/' directly, then it should not be a problem. but if the user access '/products' directly, can we ensure the "this.props.fetchInitialSettings();" is done first on the "app" then continue rendering the children component?

I've tried to modified App/Component.js to look like this:

class CoreLayout extends React.Component {
  componentWillMount() {
    this.props.fetchInitialSettings();
  }

  componentDidMount() {
    this.node = ReactDOM.findDOMNode(this.refs.container);
    this.renderContent();
  }

  componentWillReceiveProps(newProps) {
    this.renderContent(newProps);
  }

  renderContent(props) {
    props = props || this.props;

    if (props.firstLoadDone) {
      ReactDOM.render(<Provider store={props.store}>{props.children}</Provider>, this.node);
    }
    else {
      ReactDOM.render(<div />, this.node);
    }
  }

  render() {
    return (
      <div>
        <Header />
        <div className='main' ref='container'></div>
        <Footer />
      </div>
    );
  }
}

But I'm not sure wether this is the right approach. I used a flag in the state, "firstLoadDone", to keep track wether fetchInitialSettings is done or not. And only after it's done, componentWillReceiveProps will be called, and then I render the children component.

However, it doesn't feel right. For example, I have to wrap the children inside "Provider" again, together with the "store" and pass down the original "store" to children component, otherwise, the children component will loose their access to original store.

And I'd like to use this approach for deeper route, for example: the user access /products/31 directly. Meaning:

  1. Render app component
  2. Wait for necessary API for app is done
  3. if done, render product component
  4. Wait for necessary API for product is done
  5. if done, render product-detail component
  6. done

Thanks in advance

Regards, Riki

rpribadi-ds avatar Jul 14 '16 06:07 rpribadi-ds

Did you ever work this out? I have a similar problem. I need to load a few json files before anything renders. It works fine in development, but when I run compile and put it on a server, the app crashes on init because the components are expecting data. I'm at my wits end, at this point.

carlwelchdesign avatar Sep 23 '16 01:09 carlwelchdesign

One can use Redux here and:

  1. Set up a reducer containing an initial state of { ... loading: true ... }, issue an action of your choice in your a component's componentDidMount() to fetch the prerequisites (assuming an async flow so most probably a thunk along with other async Redux middlewares, e.g. redux-simple-promise).

  2. Set up the render() method like such:

// context: Within a 'higher-order enough' `React.Component`
render() {
  // Don't forget to `connect` the `component` to the Redux `store` yada yada
  const { loading } = this.props;
  return (
    !loading &&
    <MyComponent />
  );
}

This is a React idiom to basically render nothing (null is also used) if the underlying 'precondition' (in this case, the loading flag) fails/is not met.

  1. Handle the response in the associated reducer (passing it down as a prop) in case of success/failure (redux-simple-promise action types are conventionally called MyActionSuccess and *Failure or so). A *Success response would move/take the state to { ... loading: false ... }.

Thus, the MyComponent component will be render-ed only if loading is false.

Dr1Ku avatar Sep 23 '16 12:09 Dr1Ku

This !loading && trick is very nice!

projenix avatar May 29 '17 16:05 projenix

So If I want to avoid doing this in all my controllers.. suppose I want to have always my nav bar in the header which should be loaded before any body component. Is wrapping the "body components" inside a component following the loading:false pattern is the way to go?

enkil2003 avatar Feb 25 '18 17:02 enkil2003

@enkil2003: Yes, that would be the way to go, my use case involved having a <Footer> type of component and other 'main' components (the body type of components, yes), which would only get rendered if loading was false.

Dr1Ku avatar Feb 25 '18 17:02 Dr1Ku