gpt4 book ai didi

javascript - 清理 React Hooks 中未安装组件的内存泄漏

转载 作者:行者123 更新时间:2023-12-03 13:08:20 26 4
gpt4 key购买 nike

我是 React 的新手,所以这可能很容易实现,但即使我做了一些研究,我自己也无法弄清楚。如果这太愚蠢了,请原谅我。

上下文

我正在使用Inertia.js使用 Laravel(后端)和 React(前端)适配器。如果您不了解惯性,它基本上是:

Inertia.js lets you quickly build modern single-page React, Vue andSvelte apps using classic server-side routing and controllers.

问题

我正在做一个简单的登录页面,其中有一个表单,提交后将执行 POST 请求来加载下一页。它似乎工作正常,但在其他页面中控制台显示以下警告:

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 a useEffectcleanup function.

in login (created by 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;

现在,我知道我必须执行清理功能,因为请求的 promise 是生成此警告的原因。我知道我应该使用 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;

最佳答案

因为是异步 Promise 调用,所以必须使用可变引用变量(带有 useRef)来检查已卸载的组件,以便下一步处理异步响应(避免内存泄漏):

Warning: Can't perform a React state update on an unmounted component.

在这种情况下您应该使用两个 React Hook:useRefuseEffect

例如,使用 useRef 时,可变变量 _isMounted 始终指向内存中的同一引用(不是局部变量)

useRef is the go-to hook if mutable variable is needed. Unlike localvariables, React makes sure same reference is returned during eachrender. If you want, it's the same with this.myVar in Class Component

示例:

const login = (props) => {
const _isMounted = useRef(true); // Initial value _isMounted = true

useEffect(() => {
return () => { // ComponentWillUnmount in Class Component
_isMounted.current = false;
}
}, []);

function handleSubmit(e) {
e.preventDefault();
setLoading(true);
ajaxCall = Inertia.post(window.route('login.attempt'), values)
.then(() => {
if (_isMounted.current) { // Check always mounted component
// continue treatment of AJAX response... ;
}
)
}
}
<小时/>

同时,让我向您解释一下这里使用的 React Hooks 的更多信息。另外,我还将比较功能组件(React >16.8)中的 React Hooks 与类组件中的 LifeCycle。

useEffect : Most side-effects happen inside the hook. Examples of side effects are : data fetching, setting up a subscription, and manually changing the DOM React components. The useEffect replaces a lot of LifeCycles in Class Component (componentDidMount, componentDidUpate, componentWillUnmount)

 useEffect(fnc, [dependency1, dependency2, ...]); // dependencies array argument is optional
  1. useEffect 的默认行为在第一次渲染后运行(如 ComponentDidMount),并且在每次更新渲染后运行(如果您没有依赖项)(如 ComponentDidUpdate) 。就像这样:useEffect(fnc);

  2. 为 useEffect 提供一系列依赖项将改变其生命周期。在此示例中:useEffect 将在第一次渲染后以及每次计数更改时调用一次

    导出默认函数() {const [count, setCount] = useState(0);

    useEffect(fnc, [count]);

    }

  3. 如果您为依赖项放置一个空数组,则 useEffect 将仅在第一次渲染后运行一次(如 ComponentDidMount)。就像这样:useEffect(fnc, []);

  4. 为了防止资源泄漏,当钩子(Hook)的生命周期结束时,所有内容都必须被释放(如 ComponentWillUnmount)。例如,如果依赖项为空数组,则返回的函数将在组件卸载后调用。就像这样:

    useEffect(() => {返回fnc_cleanUp;//fnc_cleanUp 将取消所有订阅和异步任务(例如:clearInterval)}, []);

useRef : returns a mutable ref object whose .current property isinitialized to the passed argument (initialValue). The returned objectwill persist for the full lifetime of the component.

示例:对于上面的问题,我们不能在这里使用局部变量,因为它会丢失并在每次更新渲染时重新启动。

const login = (props) => {
let _isMounted= true; // it isn't good because of a local variable, so the variable will be lost and re-defined on every update render

useEffect(() => {
return () => {
_isMounted = false; // not good
}
}, []);

// ...
}

因此,通过结合使用 useRefuseEffect,我们可以彻底清除内存泄漏。

<小时/>

您可以阅读有关 React Hooks 的更多内容的好链接是:

[EN] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb

[法国] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/

关于javascript - 清理 React Hooks 中未安装组件的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59780268/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com