Я новичок в использовании React, так что это может быть очень просто сделать, но я не могу понять это самостоятельно, хотя я провел некоторое исследование. Прости меня, если это слишком глупо.
контекст
Я использую Inertia.js с адаптерами Laravel (backend) и React (front-end). Если вы не знаете инерцию, это в основном:
Inertia.js позволяет быстро создавать современные одностраничные приложения React, Vue и Svelte с использованием классической серверной маршрутизации и контроллеров.
вопрос
Я делаю простую страницу входа в систему, которая имеет форму, которая при отправке будет выполнять запрос POST для загрузки следующей страницы. Вроде нормально работает, но на других страницах консоль выдает следующее предупреждение:
Предупреждение. Невозможно выполнить обновление состояния React для неустановленного компонента. Это неоперация, но она указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в функции очистки useEffect.
в логине (создан Inertia)
Связанный код (я упростил его, чтобы избежать ненужных строк):
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;
Теперь я знаю, что должен выполнить функцию очистки, потому что обещание запроса - это то, что генерирует это предупреждение. Я знаю, что я должен использовать, useEffect
но я не знаю, как применить это в этом случае. Я видел пример, когда значение меняется, но как это сделать при вызове такого рода?
Заранее спасибо.
Обновить
По запросу полный код этого компонента:
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;
источник
.then(() => {})
?Ответы:
Поскольку это асинхронный вызов обещания, вы должны использовать изменяемую переменную ref (с useRef), чтобы проверить уже размонтированный компонент для следующей обработки асинхронного ответа (избегая утечек памяти):
Два React Hooks, которые вы должны использовать в этом случае:
useRef
иuseEffect
.С
useRef
, например, изменяемые переменный_isMounted
всегда направлен на одной и те же ссылки в памяти (не локальный переменной)Пример :
В этом же случае позвольте мне объяснить вам больше информации о React Hooks, используемых здесь. Также я буду сравнивать React Hooks в Функциональном Компоненте (версия React> 16.8) с LifeCycle в Компоненте Класса.
1) Поведение useEffect по умолчанию запускается как после первого рендеринга (например, ComponentDidMount), так и после каждого рендеринга обновления (например, ComponentDidUpdate), если у вас нет зависимостей. Это все равно, что :
useEffect(fnc);
2) Предоставление массива зависимостей для useEffect изменит его жизненный цикл. В этом примере: useEffect будет вызываться один раз после первого рендеринга, и каждый раз, когда изменяется счетчик
3) useEffect будет запускаться только один раз после первого рендеринга (например, ComponentDidMount), если вы поместите пустой массив для зависимости. Это все равно, что :
useEffect(fnc, []);
4) Чтобы предотвратить утечку ресурсов, все должно быть удалено, когда жизненный цикл ловушки заканчивается (например, ComponentWillUnmount) . Например, с пустым массивом зависимостей, возвращаемая функция будет вызываться после размонтирования компонента. Это все равно, что :
Пример: с вопросом выше, мы не можем использовать локальную переменную здесь, потому что она будет потеряна и перезапущена при каждом обновлении рендеринга.
Таким образом, с помощью комбинации useRef и useEffect мы можем полностью устранить утечки памяти.
Хорошие ссылки, которые вы можете прочитать больше о React Hooks:
[EN] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
[FR] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/
источник
Вы можете использовать метод cancelActiveVisits,
Inertia
чтобы отменить активнуюvisit
вuseEffect
очистке ловушку.Таким образом, с этим вызовом активный
visit
будет отменен, и состояние не будет обновлено.если
Inertia
запрос будет отменен, он вернет пустой ответ, поэтому вам нужно добавить дополнительную проверку для обработки пустого ответа. Добавьте также блок catch для обработки любых возможных ошибок.Альтернативный способ (обходной путь)
Вы можете использовать,
useRef
чтобы сохранить статус компонента и на основании этого вы можете обновитьstate
.Проблема:
Сражение показывается, потому что
handleSubmit
он пытается обновить состояние компонента, даже если компонент отключен от dom.Решение:
Установите флажок , чтобы держать статус из
component
, еслиcomponent
есть ,mounted
тоflag
значение будетtrue
и еслиcomponent
этоunmounted
значение флага будет ложным. Поэтому на основании этого мы можем обновитьstate
. Для статуса флага мы можем использоватьuseRef
ссылку.И затем
useEffect
в функции очистки мы можем установить флагfalse.
функция очистки useEffecr
Пример:
И в handleSubmit мы можем проверить, смонтирован ли компонент или нет, и обновить состояние, основываясь на этом.
В противном случае установите
_componentStatus
значение null, чтобы избежать утечек памяти.источник
ajaxCall
внутриuseEffect
. и посмотрим, какова стоимостьundefined
. Я добавил это сразу послеreturn () => {