Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

689
Views
No se puede realizar una actualización de estado de React en un componente desmontado

Problema

Estoy escribiendo una aplicación en React y no pude evitar un escollo muy común, que es llamar a setState(...) después de componentWillUnmount(...) .

Examiné con mucho cuidado mi código e intenté implementar algunas cláusulas de protección, pero el problema persistió y sigo observando la advertencia.

Por lo tanto, tengo dos preguntas:

  1. ¿Cómo averiguo a partir del seguimiento de la pila , qué componente en particular y controlador de eventos o gancho del ciclo de vida es responsable de la violación de la regla?
  2. Bueno, cómo solucionar el problema en sí, porque mi código se escribió teniendo en cuenta este escollo y ya está tratando de evitarlo, pero algún componente subyacente sigue generando la advertencia.

consola del navegador

 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method. in TextLayerInternal (created by Context.Consumer) in TextLayer (created by PageInternal) index.js:1446 d/console[e] index.js:1446 warningWithoutStack react-dom.development.js:520 warnAboutUpdateOnUnmounted react-dom.development.js:18238 scheduleWork react-dom.development.js:19684 enqueueSetState react-dom.development.js:12936 ./node_modules/react/cjs/react.development.js/Component.prototype.setState react.development.js:356 _callee$ TextLayer.js:97 tryCatch runtime.js:63 invoke runtime.js:282 defineIteratorMethods/</prototype[method] runtime.js:116 asyncGeneratorStep asyncToGenerator.js:3 _throw asyncToGenerator.js:29

ingrese la descripción de la imagen aquí

Código

Libro.tsx

 import { throttle } from 'lodash'; import * as React from 'react'; import { AutoWidthPdf } from '../shared/AutoWidthPdf'; import BookCommandPanel from '../shared/BookCommandPanel'; import BookTextPath from '../static/pdf/sde.pdf'; import './Book.css'; const DEFAULT_WIDTH = 140; class Book extends React.Component { setDivSizeThrottleable: () => void; pdfWrapper: HTMLDivElement | null = null; isComponentMounted: boolean = false; state = { hidden: true, pdfWidth: DEFAULT_WIDTH, }; constructor(props: any) { super(props); this.setDivSizeThrottleable = throttle( () => { if (this.isComponentMounted) { this.setState({ pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5, }); } }, 500, ); } componentDidMount = () => { this.isComponentMounted = true; this.setDivSizeThrottleable(); window.addEventListener("resize", this.setDivSizeThrottleable); }; componentWillUnmount = () => { this.isComponentMounted = false; window.removeEventListener("resize", this.setDivSizeThrottleable); }; render = () => ( <div className="Book"> { this.state.hidden && <div className="Book__LoadNotification centered">Book is being loaded...</div> } <div className={this.getPdfContentContainerClassName()}> <BookCommandPanel bookTextPath={BookTextPath} /> <div className="Book__PdfContent" ref={ref => this.pdfWrapper = ref}> <AutoWidthPdf file={BookTextPath} width={this.state.pdfWidth} onLoadSuccess={(_: any) => this.onDocumentComplete()} /> </div> <BookCommandPanel bookTextPath={BookTextPath} /> </div> </div> ); getPdfContentContainerClassName = () => this.state.hidden ? 'hidden' : ''; onDocumentComplete = () => { try { this.setState({ hidden: false }); this.setDivSizeThrottleable(); } catch (caughtError) { console.warn({ caughtError }); } }; } export default Book;

AutoWidthPdf.tsx

 import * as React from 'react'; import { Document, Page, pdfjs } from 'react-pdf'; pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`; interface IProps { file: string; width: number; onLoadSuccess: (pdf: any) => void; } export class AutoWidthPdf extends React.Component<IProps> { render = () => ( <Document file={this.props.file} onLoadSuccess={(_: any) => this.props.onLoadSuccess(_)} > <Page pageNumber={1} width={this.props.width} /> </Document> ); }

Actualización 1: cancelar la función acelerable (todavía sin suerte)

 const DEFAULT_WIDTH = 140; class Book extends React.Component { setDivSizeThrottleable: ((() => void) & Cancelable) | undefined; pdfWrapper: HTMLDivElement | null = null; state = { hidden: true, pdfWidth: DEFAULT_WIDTH, }; componentDidMount = () => { this.setDivSizeThrottleable = throttle( () => { this.setState({ pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5, }); }, 500, ); this.setDivSizeThrottleable(); window.addEventListener("resize", this.setDivSizeThrottleable); }; componentWillUnmount = () => { window.removeEventListener("resize", this.setDivSizeThrottleable!); this.setDivSizeThrottleable!.cancel(); this.setDivSizeThrottleable = undefined; }; render = () => ( <div className="Book"> { this.state.hidden && <div className="Book__LoadNotification centered">Book is being loaded...</div> } <div className={this.getPdfContentContainerClassName()}> <BookCommandPanel BookTextPath={BookTextPath} /> <div className="Book__PdfContent" ref={ref => this.pdfWrapper = ref}> <AutoWidthPdf file={BookTextPath} width={this.state.pdfWidth} onLoadSuccess={(_: any) => this.onDocumentComplete()} /> </div> <BookCommandPanel BookTextPath={BookTextPath} /> </div> </div> ); getPdfContentContainerClassName = () => this.state.hidden ? 'hidden' : ''; onDocumentComplete = () => { try { this.setState({ hidden: false }); this.setDivSizeThrottleable!(); } catch (caughtError) { console.warn({ caughtError }); } }; } export default Book;
over 4 years ago · Santiago Trujillo
27 answers
Answer question

0

Tuve un problema similar y lo resolví:

Estaba haciendo que el usuario iniciara sesión automáticamente enviando una acción en redux (colocando el token de autenticación en el estado redux)

y luego estaba tratando de mostrar un mensaje con this.setState({succ_message: "...") en mi componente.

El componente parecía vacío con el mismo error en la consola: "componente desmontado". "pérdida de memoria", etc.

Después de leer la respuesta de Walter en este hilo

Me di cuenta de que en la tabla de enrutamiento de mi aplicación, la ruta de mi componente no era válida si el usuario inició sesión:

 {!this.props.user.token && <div> <Route path="/register/:type" exact component={MyComp} /> </div> }

Hice la ruta visible ya sea que exista el token o no.

over 4 years ago · Santiago Trujillo Report

0

Tuve un problema similar, gracias @ ford04 me ayudó.

Sin embargo, ocurrió otro error.

NÓTESE BIEN. estoy usando ganchos ReactJS

 ndex.js:1 Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.

¿Qué causa el error?

 import {useHistory} from 'react-router-dom' const History = useHistory() if (true) { history.push('/new-route'); } return ( <> <render component /> </> )

Esto no podría funcionar porque, a pesar de que está redirigiendo a una nueva página, todo el estado y los accesorios están siendo manipulados en el dom o simplemente no se detuvo el renderizado a la página anterior.

que solucion encontre

 import {Redirect} from 'react-router-dom' if (true) { return <redirect to="/new-route" /> } return ( <> <render component /> </> )
over 4 years ago · Santiago Trujillo Report

0

Basado en la respuesta de @ ford04, aquí está el mismo encapsulado en un método:

 import React, { FC, useState, useEffect, DependencyList } from 'react'; export function useEffectAsync( effectAsyncFun : ( isMounted: () => boolean ) => unknown, deps?: DependencyList ) { useEffect( () => { let isMounted = true; const _unused = effectAsyncFun( () => isMounted ); return () => { isMounted = false; }; }, deps ); }

Uso:

 const MyComponent : FC<{}> = (props) => { const [ asyncProp , setAsyncProp ] = useState( '' ) ; useEffectAsync( async ( isMounted ) => { const someAsyncProp = await ... ; if ( isMounted() ) setAsyncProp( someAsyncProp ) ; }); return <div> ... ; } ;
over 4 years ago · Santiago Trujillo Report

0

Si está obteniendo datos de axios y el error aún ocurre, simplemente ajuste el setter dentro de la condición

 let isRendered = useRef(false); useEffect(() => { isRendered = true; axios .get("/sample/api") .then(res => { if (isRendered) { setState(res.data); } return null; }) .catch(err => console.log(err)); return () => { isRendered = false; }; }, []);
over 4 years ago · Santiago Trujillo Report

0

Aquí hay una solución específica de React Hooks para

Error

Advertencia: no se puede realizar una actualización de estado de React en un componente desmontado.

Solución

Puede declarar let isMounted = true dentro de useEffect , que se cambiará en la devolución de llamada de limpieza , tan pronto como se desmonte el componente. Antes de las actualizaciones de estado, ahora verifica esta variable condicionalmente:

 useEffect(() => { let isMounted = true; // note mutable flag someAsyncOperation().then(data => { if (isMounted) setState(data); // add conditional check }) return () => { isMounted = false }; // cleanup toggles value, if unmounted }, []); // adjust dependencies to your needs

 const Parent = () => { const [mounted, setMounted] = useState(true); return ( <div> Parent: <button onClick={() => setMounted(!mounted)}> {mounted ? "Unmount" : "Mount"} Child </button> {mounted && <Child />} <p> Unmount Child, while it is still loading. It won't set state later on, so no error is triggered. </p> </div> ); }; const Child = () => { const [state, setState] = useState("loading (4 sec)..."); useEffect(() => { let isMounted = true; fetchData(); return () => { isMounted = false; }; // simulate some Web API fetching function fetchData() { setTimeout(() => { // drop "if (isMounted)" to trigger error again // (take IDE, doesn't work with stack snippet) if (isMounted) setState("data fetched") else console.log("aborted setState on unmounted component") }, 4000); } }, []); return <div>Child: {state}</div>; }; ReactDOM.render(<Parent />, document.getElementById("root"));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect, useState, useRef } = React</script>

Extensión: uso personalizadoAsync useAsync

Podemos encapsular todo el repetitivo en un Hook personalizado, que anula automáticamente las funciones asíncronas en caso de que el componente se desmonte o los valores de dependencia hayan cambiado antes:

 function useAsync(asyncFn, onSuccess) { useEffect(() => { let isActive = true; asyncFn().then(data => { if (isActive) onSuccess(data); }); return () => { isActive = false }; }, [asyncFn, onSuccess]); }

 // custom Hook for automatic abortion on unmount or dependency change // You might add onFailure for promise errors as well. function useAsync(asyncFn, onSuccess) { useEffect(() => { let isActive = true; asyncFn().then(data => { if (isActive) onSuccess(data) else console.log("aborted setState on unmounted component") }); return () => { isActive = false; }; }, [asyncFn, onSuccess]); } const Child = () => { const [state, setState] = useState("loading (4 sec)..."); useAsync(simulateFetchData, setState); return <div>Child: {state}</div>; }; const Parent = () => { const [mounted, setMounted] = useState(true); return ( <div> Parent: <button onClick={() => setMounted(!mounted)}> {mounted ? "Unmount" : "Mount"} Child </button> {mounted && <Child />} <p> Unmount Child, while it is still loading. It won't set state later on, so no error is triggered. </p> </div> ); }; const simulateFetchData = () => new Promise( resolve => setTimeout(() => resolve("data fetched"), 4000)); ReactDOM.render(<Parent />, document.getElementById("root"));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect, useState, useRef } = React</script>

Más sobre la limpieza de efectos: Sobrerreaccionado: Una guía completa para usarEffect

over 4 years ago · Santiago Trujillo Report

0

Si las soluciones anteriores no funcionan, intente esto y funciona para mí:

 componentWillUnmount() { // fix Warning: Can't perform a React state update on an unmounted component this.setState = (state,callback)=>{ return; }; }
over 4 years ago · Santiago Trujillo Report

0

Sé que no estás usando el historial, pero en mi caso estaba usando el useHistory de React Router DOM, que desmonta el componente antes de que el estado persista en mi React Context Provider.

Para arreglar este problema he usado el hook withRouter anidando el componente, en mi caso export default withRouter(Login) , y dentro del componente const Login = props => { ...; props.history.push("/dashboard"); ... . También eliminé los otros props.history.push del componente, por ejemplo, if(authorization.token) return props.history.push('/dashboard') porque esto provoca un bucle, debido al estado de authorization .

Una alternativa para empujar un nuevo elemento a la historia .

over 4 years ago · Santiago Trujillo Report

0

Recibí esta advertencia posiblemente debido a que llamé a setState desde un gancho de efecto (esto se analiza en estos 3 problemas vinculados entre sí ).

De todos modos, actualizar la versión de reacción eliminó la advertencia.

over 4 years ago · Santiago Trujillo Report

0

Para eliminar: no se puede realizar una actualización de estado de React en una advertencia de componente desmontado, use el método componentDidMount bajo una condición y haga falsa esa condición en el método componentWillUnmount. Por ejemplo : -

 class Home extends Component { _isMounted = false; constructor(props) { super(props); this.state = { news: [], }; } componentDidMount() { this._isMounted = true; ajaxVar .get('https://domain') .then(result => { if (this._isMounted) { this.setState({ news: result.data.hits, }); } }); } componentWillUnmount() { this._isMounted = false; } render() { ... } }
over 4 years ago · Santiago Trujillo Report

0

Editar: me acabo de dar cuenta de que la advertencia hace referencia a un componente llamado TextLayerInternal . Es probable que ahí esté tu error. El resto de esto sigue siendo relevante, pero es posible que no solucione su problema.

1) Obtener la instancia de un componente para esta advertencia es difícil. Parece que hay alguna discusión para mejorar esto en React, pero actualmente no hay una manera fácil de hacerlo. Sospecho que la razón por la que aún no se ha creado es porque se espera que los componentes se escriban de tal manera que no sea posible establecer el estado después de desmontarlo, sin importar cuál sea el estado del componente. El problema, en lo que respecta al equipo de React, siempre está en el código del Componente y no en la instancia del Componente, por lo que obtiene el nombre del Tipo de Componente.

Esa respuesta puede ser insatisfactoria, pero creo que puedo solucionar su problema.

2) La función acelerada de Lodashes tiene un método de cancel . Llame a cancel en componentWillUnmount y deshágase de isComponentMounted . Cancelar es más "idiomáticamente" React que introducir una nueva propiedad.

over 4 years ago · Santiago Trujillo Report

0

intente cambiar setDivSizeThrottleable a

 this.setDivSizeThrottleable = throttle( () => { if (this.isComponentMounted) { this.setState({ pdfWidth: this.pdfWrapper!.getBoundingClientRect().width - 5, }); } }, 500, { leading: false, trailing: true } );
over 4 years ago · Santiago Trujillo Report

0

Inspirado por la respuesta aceptada por @ ford04, tuve un enfoque aún mejor para manejarlo, en lugar de usar useEffect dentro de useAsync cree una nueva función que devuelva una devolución de llamada para componentWillUnmount :

 function asyncRequest(asyncRequest, onSuccess, onError, onComplete) { let isMounted=true asyncRequest().then((data => isMounted ? onSuccess(data):null)).catch(onError).finally(onComplete) return () => {isMounted=false} } ... useEffect(()=>{ return asyncRequest(()=>someAsyncTask(arg), response=> { setSomeState(response) },onError, onComplete) },[])
over 4 years ago · Santiago Trujillo Report

0

Inspirado en la respuesta de @ ford04, uso este enlace, que también acepta devoluciones de llamada para el éxito, errores, finalmente y un abortFn:

 export const useAsync = ( asyncFn, onSuccess = false, onError = false, onFinally = false, abortFn = false ) => { useEffect(() => { let isMounted = true; const run = async () => { try{ let data = await asyncFn() if (isMounted && onSuccess) onSuccess(data) } catch(error) { if (isMounted && onError) onSuccess(error) } finally { if (isMounted && onFinally) onFinally() } } run() return () => { if(abortFn) abortFn() isMounted = false }; }, [asyncFn, onSuccess]) }

Si el asyncFn está realizando algún tipo de recuperación desde el back-end, a menudo tiene sentido cancelarlo cuando el componente está desmontado (aunque no siempre, a veces, si está cargando algunos datos en una tienda, también podría querer hacerlo). terminarlo incluso si el componente está desmontado)

over 4 years ago · Santiago Trujillo Report

0

El enfoque isMounted es un antipatrón en la mayoría de los casos porque en realidad no limpia ni cancela nada, solo evita cambiar el estado de los componentes desmontados, pero no hace nada con las tareas asincrónicas pendientes. El equipo de React eliminó recientemente la advertencia de fuga porque los usuarios siguen creando muchos antipatrones para ocultar la advertencia en lugar de solucionar su causa.

Pero escribir código cancelable en JS simple puede ser realmente complicado. Para arreglar esto, hice mi propia lib useAsyncEffect2 con ganchos personalizados, construida sobre una promesa cancelable ( c-promise2 ) para ejecutar código asíncrono cancelable para alcanzar su cancelación elegante. Todas las etapas asincrónicas (promesas), incluidas las profundas, son cancelables. Esto significa que la solicitud aquí se anulará automáticamente si se cancela su contexto principal. Por supuesto, se puede utilizar cualquier otra operación asíncrona en lugar de una solicitud.

  • Demostración de useAsyncEffect con uso normal de useState ( demostración en vivo ):
 import React, { useState } from "react"; import { useAsyncEffect } from "use-async-effect2"; import cpAxios from "cp-axios"; function TestComponent({url}) { const [text, setText] = useState(""); const cancel = useAsyncEffect( function* () { setText("fetching..."); const json = (yield cpAxios(url)).data; setText(`Success: ${JSON.stringify(json)}`); }, [url] ); return ( <div> <div>{text}</div> <button onClick={cancel}> Cancel request </button> </div> ); }
  • Demostración useAsyncEffect con uso de estados internos ( demostración en vivo ):
 import React from "react"; import { useAsyncEffect } from "use-async-effect2"; import cpAxios from "cp-axios"; function TestComponent({ url, timeout }) { const [cancel, done, result, err] = useAsyncEffect( function* () { return (yield cpAxios(url).timeout(timeout)).data; }, { states: true, deps: [url] } ); return ( <div> {done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."} <button onClick={cancel} disabled={done}> Cancel async effect (abort request) </button> </div> ); }
  • Componente de clase usando decoradores ( demostración en vivo )
 import React, { Component } from "react"; import { ReactComponent } from "c-promise2"; import cpAxios from "cp-axios"; @ReactComponent class TestComponent extends Component { state = { text: "" }; *componentDidMount(scope) { const { url, timeout } = this.props; const response = yield cpAxios(url).timeout(timeout); this.setState({ text: JSON.stringify(response.data, null, 2) }); } render() { return (<div>{this.state.text}</div>); } } export default TestComponent;

Más otros ejemplos:

  • Solicitud de Axios con manejo de errores
  • Obtener tiempo por coordenadas
  • Búsqueda en vivo
  • Pausa y reanudar
  • Captura de progreso
over 4 years ago · Santiago Trujillo Report

0

const handleClick = async (item: NavheadersType, index: number) => { const newNavHeaders = [...navheaders]; if (item.url) { await router.push(item.url); =>>>> line causing error (causing route to happen) // router.push(item.url); =>>> coreect line newNavHeaders.forEach((item) => (item.active = false)); newNavHeaders[index].active = true; setnavheaders([...newNavHeaders]); } };
over 4 years ago · Santiago Trujillo Report

0

La solución de @ ford04 no funcionó para mí y especialmente si necesita usar isMounted en varios lugares (por ejemplo, múltiples useEffect), se recomienda usar Ref, como se muestra a continuación:

  1. Paquetes esenciales
 "dependencies": { "react": "17.0.1", } "devDependencies": { "typescript": "4.1.5", }
  1. Mi componente Hook
 export const SubscriptionsView: React.FC = () => { const [data, setData] = useState<Subscription[]>(); const isMounted = React.useRef(true); React.useEffect(() => { if (isMounted.current) { // fetch data // setData (fetch result) return () => { isMounted.current = false; }; } } });
over 4 years ago · Santiago Trujillo Report

0

Dependiendo de cómo abra su página web, es posible que no esté provocando un montaje. Como usar un <Link/> de regreso a una página que ya estaba montada en el DOM virtual, por lo que se captura la solicitud de datos de un ciclo de vida de componenteDidMount.

over 4 years ago · Santiago Trujillo Report

0

Hay un gancho bastante común llamado useIsMounted que resuelve este problema (para componentes funcionales)...

 import { useRef, useEffect } from 'react'; export function useIsMounted() { const isMounted = useRef(false); useEffect(() => { isMounted.current = true; return () => isMounted.current = false; }, []); return isMounted; }

luego en tu componente funcional

 function Book() { const isMounted = useIsMounted(); ... useEffect(() => { asyncOperation().then(data => { if (isMounted.current) { setState(data); } }) }); ... }
over 4 years ago · Santiago Trujillo Report

0

Aquí hay una solución simple para esto. Esta advertencia se debe a que cuando realizamos una solicitud de recuperación mientras esa solicitud está en segundo plano (porque algunas solicitudes tardan un tiempo) y navegamos hacia atrás desde esa pantalla, luego reaccionan y no pueden actualizar el estado. aquí está el código de ejemplo para esto. escriba esta línea antes de cada actualización de estado.

 if(!isScreenMounted.current) return;

Aquí está el código completo

 import React , {useRef} from 'react' import { Text,StatusBar,SafeAreaView,ScrollView, StyleSheet } from 'react-native' import BASEURL from '../constants/BaseURL'; const SearchScreen = () => { const isScreenMounted = useRef(true) useEffect(() => { return () => isScreenMounted.current = false },[]) const ConvertFileSubmit = () => { if(!isScreenMounted.current) return; setUpLoading(true) var formdata = new FormData(); var file = { uri: `file://${route.params.selectedfiles[0].uri}`, type:`${route.params.selectedfiles[0].minetype}`, name:`${route.params.selectedfiles[0].displayname}`, }; formdata.append("file",file); fetch(`${BASEURL}/UploadFile`, { method: 'POST', body: formdata, redirect: 'manual' }).then(response => response.json()) .then(result => { if(!isScreenMounted.current) return; setUpLoading(false) }).catch(error => { console.log('error', error) }); } return( <> <StatusBar barStyle="dark-content" /> <SafeAreaView> <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.scrollView}> <Text>Search Screen</Text> </ScrollView> </SafeAreaView> </> ) } export default SearchScreen; const styles = StyleSheet.create({ scrollView: { backgroundColor:"red", }, container:{ flex:1, justifyContent:"center", alignItems:"center" } })
over 4 years ago · Santiago Trujillo Report

0

Resolví este problema proporcionando todos los parámetros que se usan en el gancho useEffect

El código informó el error:

 useEffect(() => { getDistrict({ geonameid: countryId, subdistrict: level, }).then((res) => { ...... }); }, [countryId]);

El código después de la corrección:

 useEffect(() => { getDistrict({ geonameid: countryId, subdistrict: level, }).then((res) => { ...... }); }, [countryId,level]);

Puedo ver que los problemas se resolvieron después de proporcionar todos los parámetros (incluido el parámetro de nivel) que se suponía que debían pasar.

over 4 years ago · Santiago Trujillo Report

0

La solución más simple y compacta (con una explicación) se ve a continuación como una solución de una sola línea.

 useEffect(() => { return () => {}; }, []);

El ejemplo anterior de useEffect() devuelve una función de devolución de llamada que activa React para finalizar su parte de desmontaje de su ciclo de vida antes de actualizar el estado.

Esa solución muy simplista es todo lo que se necesita. Además, también funciona a diferencia de la sintaxis ficticia proporcionada por @ford04 y @sfletche. Por cierto, el siguiente fragmento de código de @ford04 es una sintaxis puramente imaginaria ( @sfletche , @vinod , @guneetgstar y @Drew Cordano usaron la misma sintaxis imaginaria).

data => { <--- Sintaxis ficticia/imaginaria

 someAsyncOperation().then(data => { if (isMounted) setState(data); // add conditional check })

Todos mis linters y todos los linters de todo mi equipo no lo aceptarán y reportan Uncaught SyntaxError: unexpected token: '=>' . Me sorprende que nadie captó la sintaxis imaginaria. ¿Alguien que haya participado en este hilo de preguntas, particularmente entre los que votaron a favor, podría explicarme cómo lograron que las soluciones funcionaran para ellos?

over 4 years ago · Santiago Trujillo Report

0

ACTUALIZAR NO USE MI RESPUESTA ORIGINAL YA QUE NO FUNCIONA

Esta respuesta se basó en el uso de promesas cancelables y una nota en makecancelable que migré para usar ganchos. Sin embargo, parece que no cancela una cadena de async / await e incluso cancelable-promise no admite la cancelación de una cadena de esperas .

Investigando un poco más sobre esto, parece que algunas razones internas de Google impidieron que las promesas cancelables entraran en el estándar .

Además, hubo alguna promesa con Bluebird que introduce promesas cancelables, pero no funciona en Expo o al menos no he visto un ejemplo de que funcione en Expo.

La respuesta aceptada es la mejor. Como uso TypeScript, adapté el código con algunas modificaciones (configuré explícitamente las dependencias ya que las dependencias implícitas de la respuesta aceptada parecen dar un bucle de re-renderización en mi aplicación, agregué y usé async/await en lugar de promesa cadena, pase un referencia al objeto montado para que una cadena asíncrona/en espera se pueda cancelar antes si es necesario)

 /** * This starts an async function and executes another function that performs * React state changes if the component is still mounted after the async * operation completes * @template T * @param {(mountedRef: React.MutableRefObject<boolean>) => Promise<T>} asyncFunction async function, * it has a copy of the mounted ref so an await chain can be canceled earlier. * @param {(asyncResult: T) => void} onSuccess this gets executed after async * function is resolved and the component is still mounted * @param {import("react").DependencyList} deps */ export function useAsyncSetEffect(asyncFunction, onSuccess, deps) { const mountedRef = useRef(false); useEffect(() => { mountedRef.current = true; (async () => { const x = await asyncFunction(mountedRef); if (mountedRef.current) { onSuccess(x); } })(); return () => { mountedRef.current = false; }; }, deps); }

respuesta original

Dado que tengo muchas operaciones diferentes que son async , utilizo el paquete cancelable-promise para resolver este problema con cambios mínimos en el código.

Código anterior:

 useEffect(() => (async () => { const bar = await fooAsync(); setSomeState(bar); })(), [] );

Nuevo código:

 import { cancelable } from "cancelable-promise"; ... useEffect( () => { const cancelablePromise = cancelable(async () => { const bar = await fooAsync(); setSomeState(bar); }) return () => cancelablePromise.cancel(); }, [] );

También puede escribirlo en una función de utilidad personalizada como esta

 /** * This wraps an async function in a cancelable promise * @param {() => PromiseLike<void>} asyncFunction * @param {React.DependencyList} deps */ export function useCancelableEffect(asyncFunction, deps) { useEffect(() => { const cancelablePromise = cancelable(asyncFunction()); return () => cancelablePromise.cancel(); }, deps); }
over 4 years ago · Santiago Trujillo Report

0

Agregue una referencia a un componente jsx y luego verifique que exista

 function Book() { const ref = useRef(); useEffect(() => { asyncOperation().then(data => { if (ref.current) setState(data); }) }); return <div ref={ref}>content</div> }
over 4 years ago · Santiago Trujillo Report

0

Verificar si un componente está montado es en realidad un patrón anti según la documentación de React . La solución a la advertencia de setState es más bien aprovechar el uso de un AbortController :

 useEffect(() => { const abortController = new AbortController() // creating an AbortController fetch(url, { signal: abortController.signal }) // passing the signal to the query .then(data => { setState(data) // if everything went well, set the state }) .catch(error => { if (error.name === 'AbortError') return // if the query has been aborted, do nothing throw error }) return () => { abortController.abort() // stop the query by aborting on the AbortController on unmount } }, [])

Para las operaciones asincrónicas que no se basan en Fetch API, aún debe haber una forma de cancelar estas operaciones asincrónicas, y debería aprovecharlas en lugar de simplemente verificar si un componente está montado. Si está creando su propia API, puede implementar la API AbortController para manejarla.

Para obtener más contexto, verificar si un componente está montado es un patrón anti, ya que React verifica internamente si el componente está montado para mostrar esa advertencia . Hacer la misma verificación nuevamente es solo una forma de ocultar la advertencia, y hay algunas formas más fáciles de ocultarlas que agregar esta pieza de código en una gran parte de una base de código.

Fuente: https://medium.com/doctolib/react-stop-checking-if-your-component-is-mounted-3bb2568a4934

over 4 years ago · Santiago Trujillo Report

0

React ya eliminó esta advertencia, pero aquí hay una mejor solución (no solo una solución alternativa)

 useEffect(() => { const abortController = new AbortController() // creating an AbortController fetch(url, { signal: abortController.signal }) // passing the signal to the query .then(data => { setState(data) // if everything went well, set the state }) .catch(error => { if (error.name === 'AbortError') return // if the query has been aborted, do nothing throw error }) return () => { abortController.abort() } }, [])
over 4 years ago · Santiago Trujillo Report

0

"Si hay un gancho setter en su useEffect, devuelva una función de limpieza" - yo

Echa un vistazo a tus funciones useEffect() que tienen un gancho setter setSomething() , y haz que devuelva una función de limpieza al final. Por ejemplo:

 useEffect(() => { ... setSomething(something); // ... return () => { }; // The cleanup function we have to add, it could be empty like this }, [/*whatever deps or no deps at all*/]);
over 4 years ago · Santiago Trujillo Report

0

Tengo 2 soluciones para este error:

  1. volver :

Si está acostumbrado a usar hook y useEffect , entonces ponga un final de return de useEffect .

 useEffect(() => { window.addEventListener('mousemove', logMouseMove) return () => { window.removeEventListener('mousemove', logMouseMove) } }, [])
  1. componenteDesmontará :

Si usa el componentDidMount , coloque el componentWillUnmount junto a él.

 componentDidMount() { window.addEventListener('mousemove', this.logMouseMove) } componentWillUnmount() { window.removeEventListener('mousemove', this.logMouseMove) }
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!