Ken*_*rna 39 javascript promise inertiajs reactjs react-hooks
我是使用 React 的新手,所以这可能很容易实现,但即使我做了一些研究,我也无法自己弄清楚。如果这太愚蠢,请原谅我。
我将Inertia.js与 Laravel(后端)和 React(前端)适配器一起使用。如果你不知道惯性,它基本上是:
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;
Run Code Online (Sandbox Code Playgroud)
现在,我知道我必须执行清理功能,因为请求的承诺是生成此警告的原因。我知道我应该使用,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;
Run Code Online (Sandbox Code Playgroud)
San*_*ika 78
因为是异步 promise 调用,所以必须使用可变引用变量(带 useRef)来检查已经卸载的组件,以便下次处理异步响应(避免内存泄漏):
警告:无法对卸载的组件执行 React 状态更新。
在这种情况下你应该使用的两个 React Hooks :useRef和useEffect.
用useRef,例如,可变变量_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... ;
}
)
}
}
Run Code Online (Sandbox Code Playgroud)
在同一场合,让我向您解释有关此处使用的 React Hooks 的更多信息。另外,我将比较函数式组件中的 React Hooks(React >16.8)和类组件中的 LifeCycle。
useEffect:大多数副作用发生在钩子内部。副作用的示例包括:数据获取、设置订阅和手动更改 DOM React 组件。useEffect 替换了 Class Component 中的很多 LifeCycles(componentDidMount、componentDidUpate、componentWillUnmount)
useEffect(fnc, [dependency1, dependency2, ...]); // dependencies array argument is optional
Run Code Online (Sandbox Code Playgroud)
如果您没有依赖项,useEffect 的默认行为将在第一次渲染(如 ComponentDidMount)之后和每次更新渲染(如 ComponentDidUpdate)之后运行。就像那样 :useEffect(fnc);
为 useEffect 提供依赖项数组将改变其生命周期。在这个例子中: useEffect 将在第一次渲染后调用一次,每次计数改变
导出默认函数 () { const [count, setCount] = useState(0);
useEffect(fnc, [count]);
Run Code Online (Sandbox Code Playgroud)
}
如果您为依赖项放置一个空数组,则useEffect 将仅在第一次渲染后运行一次(如 ComponentDidMount)。就像那样 :useEffect(fnc, []);
为了防止资源泄漏,必须在钩子的生命周期结束时处理所有内容(如 ComponentWillUnmount)。例如,对于空的依赖数组,在组件卸载后将调用返回的函数。就像那样 :
useEffect(() => { return fnc_cleanUp; // fnc_cleanUp 将取消所有订阅和异步任务(例如: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-defined on every update render
useEffect(() => {
return () => {
_isMounted = false; // not good
}
}, []);
// ...
}
Run Code Online (Sandbox Code Playgroud)
因此,结合useRef和useEffect,我们可以彻底清除内存泄漏。
你可以阅读更多关于 React Hooks 的好链接是:
[CN] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
[FR] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/
在改变状态之前,你应该首先检查组件是否仍然被挂载。
正如@SanjiMika上面所说,当有一个导致此错误的异步操作时,这意味着您正在尝试在卸载后改变组件的状态。
react-use 提供钩子,你有两个选择:
选项#1:useMountedState
// check if isMounted before changing any state
const isMounted = useMountedState();
useEffect(() => {
const asyncAction = executeAsyncAction();
asyncAction.then(result => {
if (isMounted) {
// It's safe to mutate state here
}
});
}, []);
Run Code Online (Sandbox Code Playgroud)
选项#2:useUnmountPromise
/* `resolveWhileMounted` wraps your promise, and returns a promise that will resolve
* only while the component is still mounted */
const resolveWhileMounted = useUnmountPromise();
useEffect(async () => {
const asyncAction = executeAsyncAction();
resolveWhileMounted(asyncAction).then(result => {
// It's safe to mutate state here
});
}, []);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
41952 次 |
| 最近记录: |