vida react qué que podemos optimizar método example entre ejemplos desmontar con comunicacion componentes componente componentdidmount ciclo reactjs asynchronous redux

reactjs - react - Reaccionar: ¿Mostrar la pantalla de carga mientras se procesa DOM?



react js ejemplos (14)

La meta

Cuando se procesa la página html, muestre una rueda giratoria inmediatamente (mientras React carga), y escóndela después de que React esté listo.

Dado que la ruleta se representa en HTML / CSS puro (fuera del dominio React), React no debe controlar el proceso de mostrar / ocultar directamente, y la implementación debe ser transparente para React.

Solución 1 - la: pseudo-clase vacía

Dado que el render reacciona en un contenedor DOM - <div id="app"></div> , puede agregar una ruleta a ese contenedor, y cuando reaccione se cargará y procesará, la ruleta desaparecerá.

No puede agregar un elemento DOM (un div por ejemplo) dentro de la raíz de reacción, ya que React reemplazará el contenido del contenedor tan pronto como se ReactDOM.render() . Incluso si se vuelve null , el contenido aún se reemplazaría por un comentario - <!-- react-empty: 1 --> . Esto significa que si desea mostrar el cargador mientras se monta el componente principal, los datos se están cargando, pero en realidad no se representa nada, se coloca un marcado del cargador dentro del contenedor ( <div id="app"><div class="loader"></div></div> por ejemplo) no funcionaría.

Una solución alternativa es agregar la clase spinner al contenedor de reacción y usar la pseudo clase :empty . La rueda giratoria será visible, siempre que no se muestre nada en el contenedor (los comentarios no cuentan). Tan pronto como reacciona representa algo más que comentario, el cargador desaparecerá.

Ejemplo 1

En el ejemplo, puede ver un componente que se vuelve null hasta que esté listo. El contenedor también es el cargador: <div id="app" class="app"></div> , y la clase del cargador solo funcionará si está :empty (ver comentarios en el código):

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 -->

Ejemplo 2

Una variación sobre el uso de :empty pseudo clase :empty para mostrar / ocultar un selector, es configurar la ruleta como un elemento hermano para el contenedor de la aplicación, y mostrarla siempre que el contenedor esté vacío usando el combinador de hermanos adyacente ( + ):

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>

Solución 2 - Pase los "controladores" de la ruleta como accesorios

Para tener un control más detallado sobre el estado de visualización de los hilanderos, cree dos funciones showSpinner y hideSpinner , y hideSpinner al contenedor raíz mediante accesorios. Las funciones pueden manipular el DOM o hacer lo que sea necesario para controlar la ruleta. De esta manera, React no es consciente del "mundo exterior", ni necesita controlar el DOM directamente. Puede reemplazar fácilmente las funciones para la prueba, o si necesita cambiar la lógica, y puede pasarlas a otros componentes en el árbol React.

Ejemplo 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>

Ejemplo 2 - ganchos

Este ejemplo utiliza el gancho useEffect para ocultar la ruleta después de que se monte el componente.

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>

Este es un ejemplo de la página de la aplicación Google Adsense. La pantalla de carga mostrada antes de la página principal mostrada después.

No sé cómo hacer lo mismo con React porque si hago que la pantalla de carga sea procesada por el componente React, no se muestra mientras se carga la página porque tiene que esperar a que DOM se procese antes.

Actualizado :

Hice un ejemplo de mi enfoque colocando el cargador de pantalla en index.html y eliminándolo en el método de ciclo de vida React componentDidMount() .

Ejemplo: https://nguyenbathanh.github.io

Fuente: https://github.com/nguyenbathanh/react-loading-screen


Cuando su aplicación React es masiva, realmente toma tiempo para que se ponga en funcionamiento después de que se haya cargado la página. Digamos que #app tu parte React de la aplicación en #app . Por lo general, este elemento en su index.html es simplemente un div vacío:

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

Lo que puedes hacer en su lugar es poner un poco de estilo y un montón de imágenes allí para que se vea mejor entre la carga de la página y la representación inicial de la aplicación React a 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>

Después de cargar la página, el usuario verá inmediatamente el contenido original de index.html. Poco después, cuando React esté listo para montar toda la jerarquía de componentes renderizados en este nodo DOM, el usuario verá la aplicación real.

Nota class , no className . Es porque necesitas poner esto en tu archivo html.

Si usa SSR, las cosas son menos complicadas porque el usuario realmente verá la aplicación real justo después de que se cargue la página.


Edite la ubicación de su archivo index.html en la carpeta pública . Copie su imagen en la misma ubicación que index.html en la carpeta pública. Y luego reemplace la parte del contenido de index.html que contiene las <div id="root"> </div> por el siguiente código html dado.

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;

El logotipo ahora aparecerá en el medio de la página durante el proceso de carga. Y luego será reemplazado después de unos segundos por React.


El inicio de la aplicación Reaccionar se basa en la descarga del paquete principal. La aplicación React solo comienza después de que el paquete principal se descarga en el navegador. Esto es incluso cierto en el caso de la arquitectura de carga diferida. Pero el hecho es que no podemos decir exactamente el nombre de ningún paquete. Debido a que webapck agregará un valor hash al final de cada paquete en el momento en que ejecute el comando ''npm run build''. Por supuesto, podemos evitar eso cambiando la configuración de hash, pero afectará seriamente el problema de los datos de caché en el navegador. Es posible que los navegadores no tomen la nueva versión debido al mismo nombre de paquete. . Necesitamos un enfoque webpack + js + CSS para manejar esta situación.

cambie public / index.html como se muestra a continuación

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 }); } }; };

En su configuración de producción de webapack, cambie la opción HtmlWebpackPlugin a continuación

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(); } }

Es posible que necesite usar el comando ''expulsar'' para obtener el archivo de configuración. El último paquete web podría tener la opción de configurar HtmlWebpackPlugin sin expulsar el proyecto.


Establecer el tiempo de espera en componentDidMount funciona pero en mi aplicación recibí una advertencia de pérdida de memoria. Intenta algo como esto.

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; } }


Esto se puede hacer colocando el ícono de carga en su archivo html (index.html para ej.), De modo que los usuarios vean el ícono inmediatamente después de cargar el archivo html.

Cuando su aplicación termina de cargarse, simplemente puede eliminar ese ícono de carga en un enlace de ciclo de vida, generalmente lo hago en componentDidMount .


Esto sucederá antes de que ReactDOM.render() tome el control de la raíz <div> . Es decir, su aplicación no se habrá montado hasta ese punto.

Entonces puede agregar su cargador en su archivo index.html dentro de la raíz <div> . Y eso será visible en la pantalla hasta que React se haga cargo.

Puede usar cualquier elemento del cargador que funcione mejor para usted ( svg con animación, por ejemplo).

No es necesario eliminarlo en ningún método de ciclo de vida. React reemplazará a cualquier elemento secundario de su raíz <div> con su <App/> renderizada, como podemos ver en el GIF a continuación.

Ejemplo en CodeSandbox

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

Usar el debugger para inspeccionar la página antes de que se ReactDOM.render() .

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);


Estoy usando el paquete react-progress-2 npm, que es de dependencia cero y funciona muy bien en ReactJS.

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

Instalación:

npm install react-progress-2

Incluya react-progress-2 / main.css a su proyecto.

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

Incluya react-progress-2 y colóquelo en algún lugar del componente superior, por ejemplo:

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> ); } });

Ahora, cada vez que necesite mostrar un indicador, simplemente llame a Progress.show() , por ejemplo:

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

Tenga en cuenta que las llamadas show y hide están apiladas, por lo que después de mostrar llamadas n consecutivas, debe hacer n ocultar llamadas para ocultar un indicador o puede usar Progress.hideAll() .


Hoy en día también podemos usar ganchos en React 16.8:

<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>


La pregunta más importante es: ¿qué quiere decir con "carga"? Si está hablando del elemento físico que se está montando, algunas de las primeras respuestas aquí son geniales. Sin embargo, si lo primero que hace su aplicación es verificar la autenticación, lo que realmente está cargando son datos del backend, ya sea que el usuario haya pasado una cookie que los etiqueta como usuarios autorizados o no autorizados.

Esto se basa en redux, pero puede cambiarlo fácilmente al modelo de estado de reacción simple.

creador de acción:

<!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>

La parte final significa si el usuario está autenticado o no, la pantalla de carga desaparece después de recibir una respuesta.

Así es como se vería un componente que lo carga:

new HtmlWebpackPlugin({ inject: false, ...

Si state.loading es verdadero, siempre veremos una pantalla de carga. En componentDidMount, llamamos a nuestra función getTodos, que es un creador de acciones que convierte el estado en carga falsa cuando recibimos una respuesta (que puede ser un error). Nuestros componentes se actualizan, las llamadas se procesan nuevamente, y esta vez no hay pantalla de carga debido a la declaración if.


La solución para esto es:

En su función de render, haga algo como esto:

constructor() { this.state = { isLoading: true } } componentDidMount() { this.setState({isLoading: false}) } render() { return( this.state.isLoading ? *showLoadingScreen* : *yourPage()* ) }

Inicializar isLoading como verdadero en el constructor y falso en componentDidMount


Si alguien busca una biblioteca de acceso directo, configuración cero y dependencias cero para el caso de uso anterior, intente pace.js ( http://github.hubspot.com/pace/docs/welcome/ ).

Se conecta automáticamente a eventos (ajax, readyState, historial de estado, bucle de eventos js, etc.) y muestra un cargador personalizable.

Funcionó bien con nuestros proyectos de reacción / retransmisión (maneja los cambios de navegación usando react-router, solicitudes de retransmisión) (No afiliado; había usado pace.js para nuestros proyectos y funcionó muy bien)


También estoy usando React en mi aplicación. Para las solicitudes que estoy usando interceptores axios, una excelente manera de hacer la pantalla del cargador (página completa como mostraste un ejemplo) es agregar clase o id a, por ejemplo, el cuerpo dentro de los interceptores (aquí el código de la documentación oficial con algún código personalizado):

// 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); });

Y luego simplemente implemente en CSS su cargador con pseudo-elementos (o agregue clase o identificación a un elemento diferente, no el cuerpo como desee): puede establecer el color de fondo en opaco o transparente, etc. Ejemplo:

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


Tuve que lidiar con ese problema recientemente y se me ocurrió una solución, que funciona bien para mí. Sin embargo, probé la solución @Ori Drori anterior y desafortunadamente no funcionó correctamente (tuve algunos retrasos + No me gusta el uso de la función setTimeout allí).

Esto es lo que se me ocurrió:

archivo index.html

Etiqueta interior de la head : estilos para el indicador:

<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>

Ahora la etiqueta del body :

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

Y luego viene una lógica muy simple, dentro del archivo app.js (en la función de renderizado):

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

Como funciona

Cuando el primer componente (en mi aplicación, también es app.js en la mayoría de los casos) se monta correctamente, la spinner se oculta al aplicarle hidden atributo hidden .

Lo que es más importante agregar: la !spinner.hasAttribute(''hidden'') impide agregar hidden atributo hidden a la ruleta con cada montaje de componentes, por lo que en realidad se agregará solo una vez, cuando se cargue toda la aplicación.