Je suis nouveau avec React, donc cela peut être très simple à réaliser, mais je ne peux pas le comprendre par moi-même même si j'ai fait des recherches. Pardonnez-moi si c'est trop stupide.
Le contexte
J'utilise Inertia.js avec les adaptateurs Laravel (backend) et React (front-end). Si vous ne connaissez pas l'inertie, c'est essentiellement:
Inertia.js vous permet de créer rapidement des applications modernes React, Vue et Svelte sur une seule page à l'aide d'un routage et de contrôleurs côté serveur classiques.
Problème
Je fais une simple page de connexion qui a un formulaire qui, une fois soumis, exécutera une demande POST pour charger la page suivante. Cela semble fonctionner correctement, mais dans d'autres pages, la console affiche l'avertissement suivant:
Avertissement: impossible d'effectuer une mise à jour de l'état React sur un composant non monté. Il s'agit d'un no-op, mais cela indique une fuite de mémoire dans votre application. Pour résoudre ce problème, annulez tous les abonnements et les tâches asynchrones dans une fonction de nettoyage useEffect.
en connexion (créé par Inertia)
Le code associé (je l'ai simplifié pour éviter les lignes non pertinentes):
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;
Maintenant, je sais que je dois faire une fonction de nettoyage car la promesse de la demande est ce qui génère cet avertissement. Je sais que je devrais utiliseruseEffect
mais je ne sais pas comment l'appliquer dans ce cas. J'ai vu un exemple quand une valeur change, mais comment le faire dans un appel de ce genre?
Merci d'avance.
Mise à jour
Comme demandé, le code complet de ce composant:
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;
la source
.then(() => {})
?Réponses:
Parce que c'est l'appel de promesse asynchrone, vous devez donc utiliser une variable ref mutable (avec useRef) pour vérifier le composant déjà non monté pour le prochain traitement de la réponse asynchrone (en évitant les fuites de mémoire):
Deux crochets React que vous devez utiliser dans ce cas:
useRef
etuseEffect
.Avec
useRef
, par exemple, la variable mutable_isMounted
pointe toujours vers la même référence en mémoire (pas une variable locale)Exemple :
À la même occasion, permettez-moi de vous expliquer plus d'informations sur les crochets React utilisés ici. Je comparerai également les crochets React dans le composant fonctionnel (la version React> 16.8) avec le composant LifeCycle in Class.
1) Le comportement par défaut de useEffect s'exécute à la fois après le premier rendu (comme ComponentDidMount) et après chaque rendu de mise à jour (comme ComponentDidUpdate) si vous n'avez pas de dépendances. C'est comme ça :
useEffect(fnc);
2) Donner un tableau de dépendances à useEffect changera son cycle de vie. Dans cet exemple: useEffect sera appelé une fois après le premier rendu et à chaque changement de compte
3) useEffect ne s'exécutera qu'une seule fois après le premier rendu (comme ComponentDidMount) si vous mettez un tableau vide pour la dépendance. C'est comme ça :
useEffect(fnc, []);
4) Pour éviter les fuites de ressources, tout doit être éliminé à la fin du cycle de vie d'un crochet (comme ComponentWillUnmount) . Par exemple, avec le tableau de dépendances vide, la fonction retournée sera appelée après le démontage du composant. C'est comme ça :
Exemple: avec la question ci-dessus, nous ne pouvons pas utiliser ici une variable locale car elle sera perdue et relancée à chaque rendu de mise à jour.
Ainsi, avec la combinaison de useRef et useEffect , nous pourrions complètement nettoyer les fuites de mémoire.
Les bons liens que vous pourriez lire sur les crochets React sont:
[EN] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
[FR] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/
la source
Vous pouvez utiliser la méthode 'cancelActiveVisits'
Inertia
pour annuler le hook actifvisit
dans leuseEffect
nettoyage.Ainsi, avec cet appel, l'actif
visit
sera annulé et l'état ne sera pas mis à jour.si la
Inertia
demande est annulée, elle retournera une réponse vide, vous devez donc ajouter une vérification supplémentaire pour gérer la réponse vide. Ajoutez également un bloc catch pour gérer les erreurs potentielles.Autre moyen (solution de contournement)
Vous pouvez utiliser
useRef
pour conserver le statut du composant et sur cette base, vous pouvez mettre à jour lestate
.Problème:
La guerre s'affiche parce que le
handleSubmit
tente de mettre à jour l'état du composant même si le composant a été démonté du dom.Solution:
Définissez un indicateur pour conserver le statut de
component
, si l'component
estmounted
alors laflag
valeur seratrue
et si l'component
estunmounted
la valeur d'indicateur sera fausse. Donc, sur cette base, nous pouvons mettre à jour lestate
. Pour l'état du drapeau, nous pouvons utiliseruseRef
pour conserver une référence.Et puis dans
useEffect
la fonction de nettoyage, nous pouvons définir le drapeau surfalse.
Fonction de nettoyage useEffecr
Exemple:
Et dans handleSubmit, nous pouvons vérifier si le composant est monté ou non et mettre à jour l'état en fonction de cela.
Sinon, définissez le
_componentStatus
sur null pour éviter toute fuite de mémoire.la source
ajaxCall
intérieuruseEffect
. et voyez quelle est la valeurundefined
. Je l'ai ajouté juste après lereturn () => {