web-dev-qa-db-fra.com

React - Afficher l'écran de chargement pendant le rendu de DOM?

Ceci est un exemple tiré de la page d’application Google Adsense. L’écran de chargement affiché avant la page principale s’affiche après.

enter image description here

Je ne sais pas comment faire la même chose avec React, car si l'écran de chargement est rendu par le composant React, il ne s'affiche pas pendant le chargement de la page, car il doit attendre pour DOM rendu avant.

Mis à jour :

J'ai fait un exemple de mon approche en mettant le chargeur d'écran dans index.html et le supprimant dans la méthode React componentDidMount().

Exemple et réaction-chargement-écran .

107
Thanh Nguyen

Cela pourrait être fait en plaçant l'icône de chargement dans votre fichier html (index.html par exemple), afin que les utilisateurs la voient immédiatement après le chargement du fichier html.

Lorsque le chargement de votre application est terminé, vous pouvez simplement supprimer cette icône de chargement dans un crochet de cycle de vie, ce que je fais généralement dans componentDidMount.

75
kkkkkkk

Le but

Lorsque la page html est restituée, affichez immédiatement une roulette (tandis que React se charge) et masquez-la une fois que React est prêt.

Puisque le spinner est rendu en HTML/CSS pur (en dehors du domaine React _), React ne doit pas contrôler directement le processus d'affichage/de masquage et l'implémentation doit être transparente pour React. .

Solution 1 - la: pseudo-classe vide

Dans la mesure où vous effectuez le rendu dans un conteneur DOM - _<div id="app"></div>_, vous pouvez ajouter un filtre à ce conteneur et, lorsque Rea va se charger et générer un rendu, le filtre disparaîtra.

Vous ne pouvez pas ajouter d'élément DOM (un div par exemple) à la racine de réaction, car React remplacera le contenu du conteneur dès que ReactDOM.render() sera appelé. Même si vous rendez null, le contenu sera toujours remplacé par un commentaire - _<!-- react-empty: 1 -->_. Cela signifie que si vous souhaitez afficher le chargeur pendant le montage du composant principal, le chargement des données est en cours, mais rien n'est rendu, un marquage de chargeur placé à l'intérieur du conteneur (_<div id="app"><div class="loader"></div></div>_ par exemple) ne fonctionnerait pas.

Une solution de contournement consiste à ajouter la classe spinner au conteneur de réactions et à utiliser la pseudo-classe :empty . Le compteur sera visible tant que rien ne sera rendu dans le conteneur (les commentaires ne comptent pas). Dès que réagit rend autre chose qu'un commentaire, le chargeur disparaîtra.

Exemple 1

Dans l'exemple, vous pouvez voir un composant qui rend null jusqu'à ce qu'il soit prêt. Le conteneur est également le chargeur - _<div id="app" class="app"></div>_, et la classe du chargeur ne fonctionnera que si c'est _:empty_ (voir les commentaires dans le code):

_class App extends React.Component {
  state = {
    loading: true
  };

  componentDidMount() {
    // this simulates an async action, after which the component will render the content
    demoAsyncCall().then(() => this.setState({ loading: false }));
  }
  
  render() {
    const { loading } = this.state;
    
    if(loading) { // if your component doesn't have to wait for an async action, remove this block 
      return null; // render null when app is not ready
    }
    
    return (
      <div>I'm the app</div>
    ); 
  }
}

function demoAsyncCall() {
  return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);_
_.loader:empty {
  position: absolute;
  top: calc(50% - 4em);
  left: calc(50% - 4em);
  width: 6em;
  height: 6em;
  border: 1.1em solid rgba(0, 0, 0, 0.2);
  border-left: 1.1em solid #000000;
  border-radius: 50%;
  animation: load8 1.1s infinite linear;
}

@keyframes load8 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}_
_<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>

<div id="app" class="loader"></div> <!-- add class loader to container -->_

Exemple 2

Une variante de l’utilisation de la pseudo-classe _:empty_ pour afficher/masquer un sélecteur définit le compteur en tant qu’élément frère du conteneur app et l’affiche tant que le conteneur est vide à l’aide de combinateur frère/sœur adjacent (_+_):

_class App extends React.Component {
  state = {
    loading: true
  };

  componentDidMount() {
    // this simulates an async action, after which the component will render the content
    demoAsyncCall().then(() => this.setState({ loading: false }));
  }
  
  render() {
    const { loading } = this.state;
    
    if(loading) { // if your component doesn't have to wait for async data, remove this block 
      return null; // render null when app is not ready
    }
    
    return (
      <div>I'm the app</div>
    ); 
  }
}

function demoAsyncCall() {
  return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);_
_#app:not(:empty) + .sk-cube-grid {
  display: none;
}

.sk-cube-grid {
  width: 40px;
  height: 40px;
  margin: 100px auto;
}

.sk-cube-grid .sk-cube {
  width: 33%;
  height: 33%;
  background-color: #333;
  float: left;
  animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
}

.sk-cube-grid .sk-cube1 {
  animation-delay: 0.2s;
}

.sk-cube-grid .sk-cube2 {
  animation-delay: 0.3s;
}

.sk-cube-grid .sk-cube3 {
  animation-delay: 0.4s;
}

.sk-cube-grid .sk-cube4 {
  animation-delay: 0.1s;
}

.sk-cube-grid .sk-cube5 {
  animation-delay: 0.2s;
}

.sk-cube-grid .sk-cube6 {
  animation-delay: 0.3s;
}

.sk-cube-grid .sk-cube7 {
  animation-delay: 0s;
}

.sk-cube-grid .sk-cube8 {
  animation-delay: 0.1s;
}

.sk-cube-grid .sk-cube9 {
  animation-delay: 0.2s;
}

@keyframes sk-cubeGridScaleDelay {
  0%,
  70%,
  100% {
    transform: scale3D(1, 1, 1);
  }
  35% {
    transform: scale3D(0, 0, 1);
  }
}_
_<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>

<div id="app"></div>
<!-- add class loader to container -->

<div class="sk-cube-grid">
  <div class="sk-cube sk-cube1"></div>
  <div class="sk-cube sk-cube2"></div>
  <div class="sk-cube sk-cube3"></div>
  <div class="sk-cube sk-cube4"></div>
  <div class="sk-cube sk-cube5"></div>
  <div class="sk-cube sk-cube6"></div>
  <div class="sk-cube sk-cube7"></div>
  <div class="sk-cube sk-cube8"></div>
  <div class="sk-cube sk-cube9"></div>
</div>_

Solution 2 - Passez les "gestionnaires" de spinner comme accessoires

Pour avoir un contrôle plus fin sur l’affichage des fileuses, créez deux fonctions showSpinner et hideSpinner et transmettez-les au conteneur racine via les accessoires. Les fonctions peuvent manipuler le DOM ou faire tout ce qui est nécessaire pour contrôler le disque. De cette manière, React ne connaît pas le "monde extérieur" et n'a pas besoin de contrôler directement le DOM. Vous pouvez facilement remplacer les fonctions à des fins de test ou si vous devez modifier la logique et les transmettre à d'autres composants de l'arborescence React.

Exemple 1

_const loader = document.querySelector('.loader');

// if you want to show the loader when React loads data again
const showLoader = () => loader.classList.remove('loader--hide');

const hideLoader = () => loader.classList.add('loader--hide');

class App extends React.Component {
  componentDidMount() {
    this.props.hideLoader();
  }
  
  render() {   
    return (
      <div>I'm the app</div>
    ); 
  }
}

// the setTimeout simulates the time it takes react to load, and is not part of the solution
setTimeout(() => 
  // the show/hide functions are passed as props
  ReactDOM.render(
    <App
      hideLoader={hideLoader}
      showLoader={showLoader} 
      />,
    document.getElementById('app')
  )
, 1000);_
_.loader {
  position: absolute;
  top: calc(50% - 4em);
  left: calc(50% - 4em);
  width: 6em;
  height: 6em;
  border: 1.1em solid rgba(0, 0, 0, 0.2);
  border-left: 1.1em solid #000000;
  border-radius: 50%;
  animation: load8 1.1s infinite linear;
  transition: opacity 0.3s;
}

.loader--hide {
  opacity: 0;
}

@keyframes load8 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}_
_<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>

<div id="app"></div>

<div class="loader"></div>_

Exemple 2 - crochets

Cet exemple utilise le crochet useEffect pour masquer le compteur après le montage du composant.

_const { useEffect } = React;

const loader = document.querySelector('.loader');

// if you want to show the loader when React loads data again
const showLoader = () => loader.classList.remove('loader--hide');

const hideLoader = () => loader.classList.add('loader--hide');

const App = ({ hideLoader }) => {
  useEffect(() => hideLoader(), []);
  
  return (
    <div>I'm the app</div>
  ); 
}

// the setTimeout simulates the time it takes react to load, and is not part of the solution
setTimeout(() => 
  // the show/hide functions are passed as props
  ReactDOM.render(
    <App
      hideLoader={hideLoader}
      showLoader={showLoader} 
      />,
    document.getElementById('app')
  )
, 1000);_
_.loader {
  position: absolute;
  top: calc(50% - 4em);
  left: calc(50% - 4em);
  width: 6em;
  height: 6em;
  border: 1.1em solid rgba(0, 0, 0, 0.2);
  border-left: 1.1em solid #000000;
  border-radius: 50%;
  animation: load8 1.1s infinite linear;
  transition: opacity 0.3s;
}

.loader--hide {
  opacity: 0;
}

@keyframes load8 {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}_
_<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="app"></div>

<div class="loader"></div>_
118
Ori Drori

La solution de contournement est la suivante:

Dans votre fonction de rendu, faites quelque chose comme ça:

constructor() {
    this.state = { isLoading: true }
}

componentDidMount() {
    this.setState({isLoading: false})
}

render() {
    return(
        this.state.isLoading ? *showLoadingScreen* : *yourPage()*
    )
}

Initialiser isLoading avec true dans le constructeur et false sur composantDidMount

29
Amoolya S Kumar

Si vous recherchez une bibliothèque en accès direct, zéro configuration et zéro dépendances pour le cas d'utilisation ci-dessus, essayez pace.js ( http://github.hubspot.com/pace/docs/welcome/ ).

Il se connecte automatiquement aux événements (ajax, readyState, historique pushstate, boucle d'événements js, etc.) et affiche un chargeur personnalisable.

Nous avons bien travaillé avec nos projets react/relay (gère les modifications de navigation à l’aide de react-router, demandes de relais) (Non associé; ayant utilisé pace.js pour nos projets, cela a très bien fonctionné)

12
Hafiz Ismail

Lorsque votre application React est massive, il faut vraiment du temps pour qu'elle soit opérationnelle une fois la page chargée. Disons que vous montez votre React partie de l'application sur #app. Généralement, cet élément de votre index.html est simplement un div vide:

<div id="app"></div>

Ce que vous pouvez faire à la place, c’est de mettre un peu de style et un tas d’images pour que l’apparence soit meilleure entre le chargement de la page et le rendu initial de React app vers DOM:

<div id="app">
  <div class="logo">
    <img src="/my/cool/examplelogo.svg" />
  </div>
  <div class="preload-title">
    Hold on, it's loading!
  </div>
</div>

Une fois la page chargée, l’utilisateur verra immédiatement le contenu original de index.html. Peu de temps après, lorsque React est prêt à monter toute la hiérarchie des composants rendus sur ce nœud DOM, l'utilisateur verra l'application réelle.

Remarque class, pas className. C'est parce que vous devez mettre cela dans votre fichier html.


Si vous utilisez SSR, les choses sont moins compliquées, car l'utilisateur verra l'application réelle juste après le chargement de la page.

9
Fleischpflanzerl

Cela se produira avant que ReactDOM.render() ne prenne le contrôle de la racine <div>. C'est à dire. votre application n'aura pas été montée jusqu'à ce point.

Vous pouvez donc ajouter votre chargeur dans votre fichier index.html à la racine <div>. Et cela sera visible à l'écran jusqu'à ce que React prenne le relais.

Vous pouvez utiliser l'élément de chargeur qui vous convient le mieux (svg avec animation par exemple).

Vous n'avez pas besoin de le supprimer dans aucune méthode de cycle de vie. React remplacera tous les enfants de sa racine <div> par votre rendu <App/>, comme on peut le voir dans le GIF ci-dessous.

Exemple sur CodeSandbox

enter image description here

index.html

<head>
  <style>
    .svgLoader {
      animation: spin 0.5s linear infinite;
      margin: auto;
    }
    .divLoader {
      width: 100vw;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }
  </style>
</head>

<body>
  <div id="root">
    <div class="divLoader">
      <svg class="svgLoader" viewBox="0 0 1024 1024" width="10em" height="10em">
        <path fill="lightblue"
          d="PATH FOR THE LOADER ICON"
        />
      </svg>
    </div>
  </div>
</body>

index.js

Utilisation de debugger pour inspecter la page avant que ReactDOM.render() ne soit exécuté.

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

debugger; // TO INSPECT THE PAGE BEFORE 1ST RENDER

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
5
cbdev420

J'utilise aussi React dans mon application. Pour les requêtes, j'utilise des intercepteurs axios. Le meilleur moyen de créer un écran de chargement (une page entière comme vous avez montré un exemple) consiste à ajouter une classe ou un id à, par exemple, le corps à l'intérieur d'intercepteurs (ici le code de la documentation officielle avec un code personnalisé):

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
     document.body.classList.add('custom-loader');
     return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
       document.body.classList.remove('custom-loader');
       return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  }); 

Et ensuite, implémentez simplement en CSS votre chargeur avec des pseudo-éléments (ou ajoutez une classe ou un id à un élément différent, pas le corps à votre guise) - vous pouvez définir la couleur de l'arrière-plan sur opaque ou transparent, etc. Exemple:

custom-loader:before {
    background: #000000;
    content: "";
    position: fixed;
    ...
}

custom-loader:after {
    background: #000000;
    content: "Loading content...";
    position: fixed;
    color: white;
    ...
}
3
adrian95999

De nos jours, nous pouvons aussi utiliser des hooks dans React 16.8:

import React, { useState, useEffect } from 'react';

const app = () => {
  const [ spinner, setSpinner ] = useState(true);

  // It will be executed before rendering
  useEffect(() => {
    setTimeout(() => setSpinner(false), 1000)
  }, []);
  // [] means like componentDidMount

  return !spinner && <div >Your content</div>;
}

export default app;
3
Max Kalik

J'utilise le package react-progress-2 npm, qui est dépendant de zéro et qui fonctionne très bien dans ReactJS.

https://github.com/milworm/react-progress-2

Installation:

npm install react-progress-2

Ajoutez react-progress-2/main.css à votre projet.

import "node_modules/react-progress-2/main.css";

Incluez react-progress-2 et mettez-le quelque part dans le composant principal, par exemple:

import React from "react";
import Progress from "react-progress-2";

var Layout = React.createClass({
render: function() {
    return (
        <div className="layout">
            <Progress.Component/>
                {/* other components go here*/}
            </div>
        );
    }
});

Maintenant, chaque fois que vous devez afficher un indicateur, appelez simplement Progress.show(), par exemple:

loadFeed: function() {
    Progress.show();
    // do your ajax thing.
},

onLoadFeedCallback: function() {
    Progress.hide();
    // render feed.
}

Veuillez noter que les appels show et hide sont empilés. Ainsi, après n appels consécutifs, vous devez masquer les appels pour masquer un indicateur ou vous pouvez utiliser Progress.hideAll().

3
Alex Zavrazhniy

La définition du délai d'attente dans composantDidMount fonctionne, mais dans mon application, un avertissement de fuite de mémoire a été reçu. Essayez quelque chose comme ça.

constructor(props) {
    super(props)
    this.state = { 
      loading: true,
    }
  }
  componentDidMount() {
    this.timerHandle = setTimeout(() => this.setState({ loading: false }), 3500); 
  }

  componentWillUnmount(){
    if (this.timerHandle) {
      clearTimeout(this.timerHandle);
      this.timerHandle = 0;
    }
  }
3
Rich Costello

Récemment, j’ai dû faire face à ce problème et proposer une solution qui me convient parfaitement. Cependant, j'ai essayé la solution @Ori Drori ci-dessus et, malheureusement, cela n'a pas fonctionné correctement (avec quelques retards + je n'aime pas l'utilisation de la fonction setTimeout).

Voici ce que je suis venu avec:

Fichier index.html

Insidehead tag - styles pour l'indicateur:

<style media="screen" type="text/css">

.loading {
  -webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
  animation: sk-scaleout 1.0s infinite ease-in-out;
  background-color: black;
  border-radius: 100%;
  height: 6em;
  width: 6em;
}

.container {
  align-items: center;
  background-color: white;
  display: flex;
  height: 100vh;
  justify-content: center;
  width: 100vw;
}

@keyframes sk-scaleout {
  0% {
    -webkit-transform: scale(0);
    transform: scale(0);
  }
  100% {
    -webkit-transform: scale(1.0);
    opacity: 0;
    transform: scale(1.0);
  }
}

</style>

Maintenant la balise body:

<div id="spinner" class="container">
  <div class="loading"></div>
</div>

<div id="app"></div>

Et ensuite vient une logique très simple, à l'intérieur du fichier app.js (dans la fonction de rendu):

const spinner = document.getElementById('spinner');

if (spinner && !spinner.hasAttribute('hidden')) {
  spinner.setAttribute('hidden', 'true');
}

Comment ça marche?

Lorsque le premier composant (dans mon application, c'est app.js ainsi que dans la plupart des cas) est monté correctement, la spinner est masquée avec l'application de l'attribut hidden.

Quoi de plus important d'ajouter - !spinner.hasAttribute('hidden'), la condition empêche d'ajouter l'attribut hidden à la roulette à chaque montage de composant; il ne sera donc ajouté qu'une seule fois, lors du chargement complet de l'application.

3
kind user

Modifiez l’emplacement de votre fichier index.html dans le dossier public . Copiez votre image au même emplacement que index.html dans un dossier public. Et puis remplacez la partie du contenu de index.html contenant les balises <div id="root"> </div> par le code html indiqué ci-dessous.

<div id="root">  <img src="logo-dark300w.png" alt="Spideren" style="vertical-align: middle; position: absolute;
   top: 50%;
   left: 50%;
   margin-top: -100px; /* Half the height */
   margin-left: -250px; /* Half the width */" />  </div>

Le logo apparaîtra au milieu de la page pendant le processus de chargement. Et sera alors remplacé après quelques secondes par React.

2
Magnus Melwin

Le démarrage de react app est basé sur le téléchargement du bundle principal. L'application React ne démarre qu'après le téléchargement de l'ensemble principal dans le navigateur. Ceci est même vrai en cas d'architecture de chargement paresseux. Mais le fait est que nous ne pouvons pas préciser le nom d’un paquet. Parce que webapck ajoutera une valeur de hachage à la fin de chaque ensemble au moment où vous exécuterez la commande 'npm run build'. Bien sûr, nous pouvons éviter cela en modifiant les paramètres de hachage, mais cela affectera sérieusement le problème des données de cache dans le navigateur. Les navigateurs risquent de ne pas utiliser la nouvelle version en raison du même nom d’ensemble. . nous avons besoin d’une approche webpack + js + CSS pour gérer cette situation.

changez le fichier public/index.html comme ci-dessous

<!DOCTYPE html>
<html lang="en" xml:lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=3.0, shrink-to-fit=no">
  <meta name="theme-color" content="#000000">
  <!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
  <style>
 .percentage {
      position: absolute;
      top: 50%;
      left: 50%;
      width: 150px;
      height: 150px;
      border: 1px solid #ccc;
      background-color: #f3f3f3;
      -webkit-transform: translate(-50%, -50%);
          -ms-transform: translate(-50%, -50%);
              transform: translate(-50%, -50%);
      border: 1.1em solid rgba(0, 0, 0, 0.2);
      border-radius: 50%;
      overflow: hidden;
      display: -webkit-box;
      display: -ms-flexbox;
      display: flex;
      -webkit-box-pack: center;
          -ms-flex-pack: center;
              justify-content: center;
      -webkit-box-align: center;
          -ms-flex-align: center;
              align-items: center;
    }

    .innerpercentage {
      font-size: 20px;
    }
  </style>
  <script>
    function showPercentage(value) {
      document.getElementById('percentage').innerHTML = (value * 100).toFixed() + "%";
    }
    var req = new XMLHttpRequest();
    req.addEventListener("progress", function (event) {
      if (event.lengthComputable) {
        var percentComplete = event.loaded / event.total;
        showPercentage(percentComplete)
        // ...
      } else {
        document.getElementById('percentage').innerHTML = "Loading..";
      }
    }, false);

    // load responseText into a new script element
    req.addEventListener("load", function (event) {
      var e = event.target;
      var s = document.createElement("script");
      s.innerHTML = e.responseText;
      document.documentElement.appendChild(s);
      document.getElementById('parentDiv').style.display = 'none';

    }, false);

    var bundleName = "<%= htmlWebpackPlugin.files.chunks.main.entry %>";
    req.open("GET", bundleName);
    req.send();

  </script>
  <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->

  <title>App Name</title>
  <link href="<%= htmlWebpackPlugin.files.chunks.main.css[0] %>" rel="stylesheet">
</head>

<body>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
  <div id="parentDiv" class="percentage">
    <div id="percentage" class="innerpercentage">loading</div>
  </div>
  <div id="root"></div>
  <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
</body>

</html>

Dans votre configuration Webapack de production, modifiez l’option HtmlWebpackPlugin en dessous de

 new HtmlWebpackPlugin({
          inject: false,
...

Vous devrez peut-être utiliser la commande 'éjecter' pour obtenir le fichier de configuration. le dernier pack Web peut avoir la possibilité de configurer HtmlWebpackPlugin sans éjecter le projet. enter image description here

1

Vous n'avez pas besoin de beaucoup d'effort, voici un exemple de base.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Web site created using create-react-app" />
  <link rel="Apple-touch-icon" href="logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  <title>Title</title>
  <style>
    body {
      margin: 0;
    }

    .loader-container {
      width: 100vw;
      height: 100vh;
      display: flex;
      overflow: hidden;
    }

    .loader {
      margin: auto;
      border: 5px dotted #dadada;
      border-top: 5px solid #3498db;
      border-radius: 50%;
      width: 100px;
      height: 100px;
      -webkit-animation: spin 2s linear infinite;
      animation: spin 2s linear infinite;
    }

    @-webkit-keyframes spin {
      0% {
        -webkit-transform: rotate(0deg);
      }

      100% {
        -webkit-transform: rotate(360deg);
      }
    }

    @keyframes spin {
      0% {
        transform: rotate(0deg);
      }

      100% {
        transform: rotate(360deg);
      }
    }

  </style>
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root">
    <div class="loader-container">
      <div class="loader"></div>
    </div>
  </div>
</body>

</html>

Vous pouvez jouer avec HTML et CSS pour que cela ressemble à votre exemple.

0
Ahmed M.Kamal

La question la plus importante est: qu'entendez-vous par "chargement"? Si vous parlez de l'élément physique en cours de montage, certaines des premières réponses sont excellentes. Cependant, si la première chose que fait votre application est de vérifier l'authentification, vous chargez en réalité des données du backend, que l'utilisateur ait ou non transmis un cookie qui les identifie comme un utilisateur autorisé ou non.

Ceci est basé sur redux, mais vous pouvez facilement le changer en modèle d'état simple.

créateur d'action:

export const getTodos = () => {
  return async dispatch => {
    let res;
    try {
      res = await axios.get('/todos/get');

      dispatch({
        type: AUTH,
        auth: true
      });
      dispatch({
        type: GET_TODOS,
        todos: res.data.todos
      });
    } catch (e) {
    } finally {
      dispatch({
        type: LOADING,
        loading: false
      });
    }
  };
};

La dernière partie signifie que l'utilisateur soit autorisé ou non, l'écran de chargement disparaît après la réception d'une réponse.

Voici à quoi pourrait ressembler un composant qui le charge:

class App extends Component {
  renderLayout() {
    const {
      loading,
      auth,
      username,
      error,
      handleSidebarClick,
      handleCloseModal
    } = this.props;
    if (loading) {
      return <Loading />;
    }
    return (
      ...
    );
  }

  ...

  componentDidMount() {
    this.props.getTodos();
  }

...

  render() {
    return this.renderLayout();
 }

}

Si state.loading est la vérité, nous verrons toujours un écran de chargement. Sur composantDidMount, nous appelons notre fonction getTodos, qui est un créateur d'action qui transforme state.loading falsy lorsque nous obtenons une réponse (ce qui peut être une erreur). Notre composant est mis à jour, les appels sont rendus à nouveau et cette fois, il n'y a pas d'écran de chargement en raison de l'instruction if.

0
user3605834