我是使用React的新手,所以这可能真的很容易实现,但是即使我做了一些研究,也无法自己解决。如果这太蠢了,请原谅我。
我使用Inertia.js与Laravel(后端)和响应(前端)适配器。如果您不知道惯性,则基本上是:
Inertia.js使您可以使用经典的服务器端路由和控制器快速构建现代的单页React,Vue和Svelte应用程序。
我正在做一个简单的登录页面,该页面具有一种表单,提交后将执行POST请求以加载下一页。似乎工作正常,但在其他页面上,控制台显示以下警告:
警告:无法在已卸载的组件上执行React状态更新。这是空操作,但它表明应用程序中发生内存泄漏。要修复,请取消使用useEffect清理功能中的所有订阅和异步任务。 登录(由Inertia创建)
警告:无法在已卸载的组件上执行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但在这种情况下我不知道如何应用。我已经看到了值更改时的示例,但是如何在此类调用中执行呢?
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;
因为这是异步诺言调用,所以您必须使用一个可变的ref变量(带有useRef)来检查已卸载的组件,以进行异步响应的下一个处理(避免内存泄漏):
警告:无法在已卸载的组件上执行React状态更新。
在这种情况下,您应该使用两个React Hook:useRef和useEffect。
useRef
useRef例如,使用时,可变变量_isMounted始终指向内存中的同一引用 (而不是局部变量)
_isMounted
*如果需要可变变量, *useRef是定位钩。与局部变量不同,React确保在每次渲染期间都返回相同的引用。如果需要,类组件中的this.myVar* 相同 *
范例:
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 Hooks(版本React> 16.8)与类组件中的LifeCycle进行比较。
useEffect:大多数副作用发生在钩子内部。副作用的示例包括:数据获取,设置订阅以及在React组件中手动更改DOM。useEffect替换了类Component中的许多生命周期(componentDidMount,componentDidUpate,componentWillUnmount)
useEffect(fnc, [dependency1, dependency2, ...]); // dependencies array argument is optional
1)如果您没有依赖项,则useEffect的默认行为在第一个渲染 (例如ComponentDidMount) 之后和每个更新渲染 (例如ComponentDidUpdate) 之后运行。就像那样 :useEffect(fnc);
useEffect(fnc);
2)将依赖项数组赋予useEffect将更改其生命周期。在这个例子中:useEffect将在第一个渲染之后被调用一次,并且每次计数改变时
export default function () { const [count, setCount] = useState(0); useEffect(fnc, [count]); }
3)如果您为依赖项放置一个空数组,则useEffect将在第一个渲染_(如ComponentDidMount)_之后仅运行一次。就像那样:useEffect(fnc, []);
useEffect(fnc, []);
4)为防止资源泄漏,必须在挂钩的生命周期结束时处置所有组件(例如ComponentWillUnmount)。例如,对于空的依赖关系数组,将在组件卸载后调用返回的函数。就像那样 :
useEffect(() => { return fnc_cleanUp; // fnc_cleanUp will cancel all subscriptions and asynchronous tasks (ex. : clearInterval) }, []);
useRef* :返回 一个可变的ref对象,**该对象的.current*属性已初始化为所传递的参数(initialValue)。返回的对象将在组件的整个生命周期内保持不变。
例子:对于上面的问题,我们不能在这里使用局部变量,因为它将在每次更新渲染时丢失并重新启动。
const login = (props) => { let _isMounted= true; // it isn't good because of a local variable, so the variable will be lost and re-initiated on every update render useEffect(() => { return () => { _isMounted = false; // not good } }, []); // ... }
因此,结合 useRef 和 useEffect ,我们可以完全清除内存泄漏。