reactjs - componentwillmount - react helmet ssr
¿Está bien usar async componentDidMount()? (7)
Actualizar:
(Mi versión: React 16, Webpack 4, Babel 7):
Al usar Babel 7 descubrirás:
Usando este patrón ...
async componentDidMount() {
try {
const res = await fetch(config.discover.url);
const data = await res.json();
console.log(data);
} catch(e) {
console.error(e);
}
}
te encontrarás con el siguiente error ...
Error de referencia no capturado: regeneratorRuntime no está definido
En este caso necesitará instalar babel-plugin-transform-runtime
https://babeljs.io/docs/en/babel-plugin-transform-runtime.html
Si por alguna razón no desea instalar el paquete anterior (babel-plugin-transform-runtime), entonces querrá apegarse al patrón Promise ...
componentDidMount() {
fetch(config.discover.url)
.then(res => res.json())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
}
¿Está utilizando
componentDidMount()
como una función asíncrona una buena práctica en React Native o debería evitarlo?
Necesito obtener información de
AsyncStorage
cuando se monta el componente, pero la única forma que sé para que sea posible es hacer que el
componentDidMount()
funcione de forma asíncrona.
async componentDidMount() {
let auth = await this.getAuth();
if (auth)
this.checkAuth(auth);
}
¿Hay algún problema con eso y hay otras soluciones a este problema?
Comencemos señalando las diferencias y determinando cómo podría causar problemas.
Aquí está el código del método de ciclo de vida asincrónico y "sincronizado" del
componentDidMount()
:
// This is typescript code
componentDidMount(): void { /* do something */ }
async componentDidMount(): Promise<void> {
/* do something */
/* You can use "await" here */
}
Al mirar el código, puedo señalar las siguientes diferencias:
-
Las palabras clave
async
: en mecanografiado, esto es simplemente un marcador de código. Hace 2 cosas:-
Forzar que el tipo de retorno sea
Promise<void>
lugar devoid
. Si especifica explícitamente que el tipo de devolución no es prometedor (por ejemplo, nulo), el mecanografiado le arrojará un error. -
Le permite usar palabras clave en
await
dentro del método.
-
Forzar que el tipo de retorno sea
-
El tipo de retorno se cambia de
void
aPromise<void>
-
Significa que ahora puedes hacer esto:
async someMethod(): Promise<void> { await componentDidMount(); }
-
Significa que ahora puedes hacer esto:
-
Ahora puede usar la palabra clave
await
dentro del método y pausar temporalmente su ejecución. Me gusta esto:async componentDidMount(): Promise<void> { const users = await axios.get<string>("http://localhost:9001/users"); const questions = await axios.get<string>("http://localhost:9001/questions"); // Sleep for 10 seconds await new Promise(resolve => { setTimeout(resolve, 10000); }); // This line of code will be executed after 10+ seconds this.setState({users, questions}); return Promise.resolve(); }
Ahora, ¿cómo podrían causar problemas?
-
La palabra clave
async
es absolutamente inofensiva. -
No puedo imaginar ninguna situación en la que necesite hacer una llamada al método
componentDidMount()
, por lo que el tipo de retornoPromise<void>
es inofensivo.Llamar a un método que tenga el tipo de retorno
Promise<void>
sinawait
palabra clave no hará ninguna diferencia de llamar a uno que tenga el tipo de retorno devoid
. -
Dado que no hay métodos de ciclo de vida después de que
componentDidMount()
retrase su ejecución parece bastante seguro. Pero hay una trampa.Digamos, lo anterior
this.setState({users, questions});
sería ejecutado después de 10 segundos. En medio del tiempo de retraso, otro ...this.setState({users: newerUsers, questions: newerQuestions});
... se ejecutaron correctamente y el DOM se actualizó. El resultado fue visible para los usuarios. El reloj siguió marcando y transcurrieron 10 segundos. El retrasado
this.setState(...)
se ejecutaría y el DOM se actualizaría nuevamente, esa vez con usuarios antiguos y preguntas antiguas. El resultado también sería visible para los usuarios.
=> Es bastante seguro (no estoy seguro acerca del 100%) usar
async
con el método
componentDidMount()
.
Soy un gran admirador y hasta ahora no he encontrado ningún problema que me dé demasiado dolor de cabeza.
Creo que está bien siempre y cuando sepas lo que estás haciendo.
Pero puede ser confuso porque
async componentDidMount()
aún puede ejecutarse después de que
componentWillUnmount
haya ejecutado y el componente se haya desmontado.
También es posible que desee iniciar tareas sincrónicas y asincrónicas dentro de
componentDidMount
.
Si
componentDidMount
era asíncrono, tendría que poner todo el código síncrono antes de la primera
await
.
Puede que no sea obvio para alguien que el código anterior a la primera
await
ejecuta sincrónicamente.
En este caso, probablemente mantendría el
componentDidMount
sincrónico, pero lo llamaría métodos de sincronización y asíncrono.
Ya sea que elija
async componentDidMount()
vs sync
componentDidMount()
llamando a métodos
async
, debe asegurarse de limpiar todos los escuchas o métodos asincrónicos que aún puedan estar ejecutándose cuando el componente se desmonta.
Cuando usa
componentDidMount
sin palabra clave
async
, el documento dice esto:
Puede llamar a setState () inmediatamente en componentDidMount (). Activará una representación adicional, pero sucederá antes de que el navegador actualice la pantalla.
Si usa el
async componentDidMount
perderá esta capacidad: se producirá otro procesamiento DESPUÉS de que el navegador actualice la pantalla.
Pero, si estás pensando en usar asíncrono, como buscar datos, no puedes evitar que el navegador actualice la pantalla dos veces.
En otro mundo, no es posible PAUSAR componentDidMount antes de que el navegador actualice la pantalla
En realidad, la carga asíncrona en ComponentDidMount es un patrón de diseño recomendado a medida que React se aleja de los métodos de ciclo de vida heredados (componentWillMount, componentWillReceiveProps, componentWillUpdate) y pasa a Async Rendering.
Esta publicación de blog es muy útil para explicar por qué esto es seguro y para proporcionar ejemplos de carga asíncrona en ComponentDidMount:
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
He realizado algunas investigaciones y he encontrado una diferencia importante: React no procesa errores de los métodos asíncronos del ciclo de vida.
Entonces, si escribes algo como esto:
componentDidMount()
{
throw new Error(''I crashed!'');
}
entonces su error será detectado por el límite de errores , y puede procesarlo y mostrar un mensaje elegante.
Si cambiamos el código así:
async componentDidMount()
{
throw new Error(''I crashed!'');
}
que es equivalente a esto:
componentDidMount()
{
return Promise.reject(new Error(''I crashed!''));
}
entonces su error será tragado en silencio . La culpa es tuya, reacciona ...
Entonces, ¿cómo procesamos los errores que? La única forma parece ser una captura explícita como esta:
componentDidMount()
{
try
{
await myAsyncFunction();
}
catch(error)
{
//...
}
}
o así:
componentDidMount()
{
myAsyncFunction()
.catch(()=>
{
//...
});
}
Si aún queremos que nuestro error enriquezca el límite de error, puedo pensar en el siguiente truco:
- Detecte el error, haga que el controlador de errores cambie el estado del componente
-
Si el estado indica un error, tírelo desde el método de
render
.
Ejemplo:
class BuggyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
}
buggyAsyncfunction(){ return Promise.reject(new Error(''I crashed async!''));}
async componentDidMount() {
try
{
await this.buggyAsyncfunction();
}
catch(error)
{
this.setState({error: error});
}
}
render() {
if(this.state.error)
throw this.state.error;
return <h1>I am OK</h1>;
}
}
Tu código está bien y es muy legible para mí.
Vea este
artículo de Dale Jefferson
donde muestra un
componentDidMount
asincrónicoDidMount y se ve muy bien también.
Pero algunas personas dirían que una persona que lee el código puede asumir que React hace algo con la promesa devuelta.
Entonces la interpretación de este código y si es una buena práctica o no es muy personal.
Si quieres otra solución, puedes usar promises . Por ejemplo:
componentDidMount() {
fetch(this.getAuth())
.then(auth => {
if (auth) this.checkAuth(auth)
})
}