Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Systematic way to delay rendering until all resources are loaded

Tags:

reactjs

I use Webpack 4, Babel 7, React 16.8. My app loads google web fonts, external images required by many components taking part in the initial rendering when users load my pages.

I load fonts with a sass file like this:

@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,700');

I use images within all components like this:

import SearchSvg from '../../images/search_icon.svg';

and use them like this:

<img src={ SearchSvg } />

Now I know about <img onLoad=.....> and I know there are packages out there to test whether web fonts are already loaded. My question is: Is there any SYSTEMIC way/pattern to get the initial rendering of the React components wait until all those external resources are loaded?

Right now I use setTimeout with 500 ms to delay the root rendering in my index.js.

setTimeout(function() {
    render(
        ...
    );
}, 500);

I would LOVE to replace this hard-coded value with something that actually knows when everything's loaded -- Ideally without having to Add code in every single Component I use.

The motivation is of course to avoid Font/Image flickering when I initially render my app -- due to the rendering while images/fonts aren't fully loaded yet.

like image 292
JasonGenX Avatar asked Jun 07 '19 16:06

JasonGenX


People also ask

How do I delay rendering in React?

I think the most intuitive way to do this is by giving the children a "wait" prop , which hides the component for the duration that was passed down from the parent. By setting the default state to hidden, React will still render the component immediately, but it won't be visible until the state has changed.

What is async rendering?

Unlike with synchronous, a user visiting pages that utilize the asynchronous mode gets to see the page content without having to wait for the page to display the whole content. The type of rendering mode loads pages independently from the ads.

How wait for data before rendering React?

React does not wait to render. There is no way to make it wait. All is not lost, though. There's an easy fix. Components that render async data need to be prepared to render an empty state, at least once.

How do you stop rendering a component in React?

memo() If you're using a React class component you can use the shouldComponentUpdate method or a React. PureComponent class extension to prevent a component from re-rendering.


3 Answers

You may render your root component after onload event is fired.

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets images. https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event

window.onload = function() {
   ReactDOM.render(<App/>, document.getElementById('root'));
};

If your purpose is to increase performance then I would highly recommend to consider Server Side Rendering.

like image 70
Orkhan Huseynli Avatar answered Nov 15 '22 11:11

Orkhan Huseynli


In your case, you could use document.fonts.ready to check if the font is ready, and then conditionally render the parts you want once this is true

An example of this is found here:

https://developer.mozilla.org/en-US/docs/Web/API/Document/fonts

For your use case, you could use a similar function found at the above link, then set a state value to true if its ready. Then you could conditionally render what you want once this is true

For example:

Call the function in componentDidMount:

  componentDidMount() {
    this.isFontLoaded()
  }

The function uses document.fonts.ready, which returns a promise. We then set the state value fontReady to true once the promise returns:

  isFontLoaded = () => {
    document.fonts.ready.then(this.setState({ fontReady: true }))
  }

Only render the the things you want if fontReady is true:

{fontReady && <img src={ SearchSvg } />}
like image 35
alexr89 Avatar answered Nov 15 '22 13:11

alexr89


You can use a wrapper component to do all the checks and show a loading or nothing when that's happening and when everything is done, render the application. Something like:

class LoadApplication {
  state = { loaded: false }
  timer = null

  componentDidMount() {
    this.timer = setInterval(this.checkStuff, 50)
  }

  componentWillUnmount() {
    clearInterval(this.timer)
  }

  checkStuff() {
    if (stuffIsLoaded) {
      this.setState({ loaded: true })
    }
  }

  render() {
    return this.state.loaded ? this.props.children : null;
  }
}

...

ReactDOM.render(<LoadApplication><App /></LoadApplication>, document.querySelector('#root'))

This is kinda the same way CRA handles errors. The same way is recommended to catch errors in components in React's documentation so I think it might be what you're looking for. I hope it helps.

like image 42
MahdiPOnline Avatar answered Nov 15 '22 13:11

MahdiPOnline