Jestem nowy, używając React, więc może to być naprawdę łatwe do osiągnięcia, ale nie mogę tego sam zrozumieć, chociaż przeprowadziłem pewne badania. Wybacz mi, jeśli to jest zbyt głupie.
Kontekst
Używam Inertia.js z adapterami Laravel (backend) i React (front-end). Jeśli nie znasz bezwładności, to w zasadzie:
Inertia.js pozwala szybko tworzyć nowoczesne jednostronicowe aplikacje React, Vue i Svelte przy użyciu klasycznego routingu i kontrolerów po stronie serwera.
Kwestia
Robię prostą stronę logowania, która ma formularz, który po przesłaniu wykona żądanie POST, aby załadować następną stronę. Wygląda na to, że działa dobrze, ale na innych stronach konsola wyświetla następujące ostrzeżenie:
Ostrzeżenie: Nie można wykonać aktualizacji stanu React dla odmontowanego komponentu. To nie działa, ale oznacza wyciek pamięci w Twojej aplikacji. Aby to naprawić, anuluj wszystkie subskrypcje i zadania asynchroniczne w funkcji czyszczenia useEffect.
w login (stworzony przez Inertia)
Powiązany kod (uprościłem go, aby uniknąć niepotrzebnych wierszy):
import React, { useEffect, useState } from 'react'
import Layout from "../../Layouts/Auth";
{/** other imports */}
const login = (props) => {
const { errors } = usePage();
const [values, setValues] = useState({email: '', password: '',});
const [loading, setLoading] = useState(false);
function handleSubmit(e) {
e.preventDefault();
setLoading(true);
Inertia.post(window.route('login.attempt'), values)
.then(() => {
setLoading(false); // Warning : memory leaks during the state update on the unmounted component <--------
})
}
return (
<Layout title="Access to the system">
<div>
<form action={handleSubmit}>
{/*the login form*/}
<button type="submit">Access</button>
</form>
</div>
</Layout>
);
};
export default login;
Teraz wiem, że muszę wykonać funkcję czyszczenia, ponieważ obietnica żądania generuje to ostrzeżenie. Wiem, że powinienem użyć, useEffect
ale nie wiem, jak go zastosować w tym przypadku. Widziałem przykład zmiany wartości, ale jak to zrobić w tego rodzaju połączeniu?
Z góry dziękuję.
Aktualizacja
Zgodnie z życzeniem, pełny kod tego komponentu:
import React, { useState } from 'react'
import Layout from "../../Layouts/Auth";
import { usePage } from '@inertiajs/inertia-react'
import { Inertia } from "@inertiajs/inertia";
import LoadingButton from "../../Shared/LoadingButton";
const login = (props) => {
const { errors } = usePage();
const [values, setValues] = useState({email: '', password: '',});
const [loading, setLoading] = useState(false);
function handleChange(e) {
const key = e.target.id;
const value = e.target.value;
setValues(values => ({
...values,
[key]: value,
}))
}
function handleSubmit(e) {
e.preventDefault();
setLoading(true);
Inertia.post(window.route('login.attempt'), values)
.then(() => {
setLoading(false);
})
}
return (
<Layout title="Inicia sesión">
<div className="w-full flex items-center justify-center">
<div className="w-full max-w-5xl flex justify-center items-start z-10 font-sans text-sm">
<div className="w-2/3 text-white mt-6 mr-16">
<div className="h-16 mb-2 flex items-center">
<span className="uppercase font-bold ml-3 text-lg hidden xl:block">
Optima spark
</span>
</div>
<h1 className="text-5xl leading-tight pb-4">
Vuelve inteligente tus operaciones
</h1>
<p className="text-lg">
Recoge data de tus instalaciones de forma automatizada; accede a información histórica y en tiempo real
para que puedas analizar y tomar mejores decisiones para tu negocio.
</p>
<button type="submit" className="bg-yellow-600 w-40 hover:bg-blue-dark text-white font-semibold py-2 px-4 rounded mt-8 shadow-md">
Más información
</button>
</div>
<div className="w-1/3 flex flex-col">
<div className="bg-white text-gray-700 shadow-md rounded rounded-lg px-8 pt-6 pb-8 mb-4 flex flex-col">
<div className="w-full rounded-lg h-16 flex items-center justify-center">
<span className="uppercase font-bold text-lg">Acceder</span>
</div>
<form onSubmit={handleSubmit} className={`relative ${loading ? 'invisible' : 'visible'}`}>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-semibold mb-2" htmlFor="email">
Email
</label>
<input
id="email"
type="text"
className=" appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 outline-none focus:border-1 focus:border-yellow-500"
placeholder="Introduce tu e-mail.."
name="email"
value={values.email}
onChange={handleChange}
/>
{errors.email && <p className="text-red-500 text-xs italic">{ errors.email[0] }</p>}
</div>
<div className="mb-6">
<label className="block text-gray-700 text-sm font-semibold mb-2" htmlFor="password">
Contraseña
</label>
<input
className=" appearance-none border border-red rounded w-full py-2 px-3 text-gray-700 mb-3 outline-none focus:border-1 focus:border-yellow-500"
id="password"
name="password"
type="password"
placeholder="*********"
value={values.password}
onChange={handleChange}
/>
{errors.password && <p className="text-red-500 text-xs italic">{ errors.password[0] }</p>}
</div>
<div className="flex flex-col items-start justify-between">
<LoadingButton loading={loading} label='Iniciar sesión' />
<a className="font-semibold text-sm text-blue hover:text-blue-700 mt-4"
href="#">
<u>Olvidé mi contraseña</u>
</a>
</div>
<div
className={`absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center ${!loading ? 'invisible' : 'visible'}`}
>
<div className="lds-ellipsis">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</form>
</div>
<div className="w-full flex justify-center">
<a href="https://optimaee.com">
</a>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default login;
źródło
.then(() => {})
?Odpowiedzi:
Ponieważ jest to wywołanie obietnicy asynchronicznej, musisz więc użyć zmiennej zmiennej ref (with useRef), aby sprawdzić już odmontowany komponent do następnego przetwarzania odpowiedzi asynchronicznej (unikanie przecieków pamięci):
Dwa zaczepy reakcyjne, których należy użyć w tym przypadku:
useRef
iuseEffect
.Z
useRef
, na przykład, zmienna zmienny_isMounted
jest zawsze skierowany na samego odniesienia w pamięci (nie jest to zmienna lokalna)Przykład:
Przy tej samej okazji pozwól, że wyjaśnię ci więcej informacji na temat używanych haków React. Porównuję także React Hooks w Functional Component (wersja React> 16.8) z LifeCycle in Class Component.
1) Domyślne zachowanie useEffect działa zarówno po pierwszym renderowaniu (jak ComponentDidMount), jak i po każdym renderowaniu aktualizacji (jak ComponentDidUpdate), jeśli nie masz zależności. To tak:
useEffect(fnc);
2) Podanie tablicy zależności do użyciaEffect zmieni jego cykl życia. W tym przykładzie: useEffect zostanie wywołany jeden raz po pierwszym renderowaniu i za każdym razem zmieni się liczenie
3) useEffect uruchomi się tylko raz po pierwszym renderowaniu (jak ComponentDidMount), jeśli umieścisz pustą tablicę dla zależności. To tak:
useEffect(fnc, []);
4) Aby zapobiec wyciekom zasobów, wszystko musi zostać usunięte po zakończeniu cyklu życia haka (np. ComponentWillUnmount) . Na przykład przy pustej tablicy zależności zwracana funkcja zostanie wywołana po odmontowaniu komponentu. To tak:
Przykład: przy powyższym pytaniu nie możemy użyć zmiennej lokalnej, ponieważ zostanie ona utracona i ponownie zainicjowana przy każdym renderowaniu aktualizacji.
Tak więc, dzięki kombinacji useRef i useEffect , możemy całkowicie usunąć przecieki pamięci.
Dobre linki, które można przeczytać więcej o hakach React, to:
[EN] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
[FR] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/
źródło
Możesz użyć metody „cancelActiveVisits”,
Inertia
aby anulować aktywnyvisit
wuseEffect
haku czyszczenia.Dzięki temu połączeniu aktywne
visit
zostaną anulowane i stan nie zostanie zaktualizowany.jeśli
Inertia
żądanie zostanie anulowane, zwróci pustą odpowiedź, więc musisz dodać dodatkowe sprawdzenie, aby obsłużyć pustą odpowiedź. Dodaj również blok catch, aby obsłużyć wszelkie potencjalne błędy.Alternatywny sposób (obejście)
Możesz użyć
useRef
do utrzymania statusu komponentu i na tej podstawie możesz zaktualizowaćstate
.Problem:
Walka jest wyświetlana, ponieważ
handleSubmit
próbuje zaktualizować stan komponentu, mimo że komponent został odmontowany z domeny.Rozwiązanie:
Ustawić flagę trzymać stanu połączenia
component
, jeżelicomponent
jestmounted
toflag
wartość będzietrue
, a jeślicomponent
jestunmounted
wartość flagi będą fałszywe. Na tej podstawie możemy zaktualizowaćstate
. Dla statusu flagi możemy użyćuseRef
do przechowywania referencji.A następnie w
useEffect
funkcji czyszczenia możemy ustawić flagę nafalse.
useEffecr funkcja czyszczenia
Przykład:
I w handleSubmit możemy sprawdzić, czy komponent jest zamontowany czy nie, i zaktualizować stan na tej podstawie.
W innym przypadku ustaw
_componentStatus
wartość null, aby uniknąć wycieków pamięci.źródło
ajaxCall
wnętrzauseEffect
. i zobacz, jaka jest wartośćundefined
. Dodałem go zaraz poreturn () => {