Reactjs 要修复此问题,请取消useEffect清理函数中的所有订阅和异步任务
我有这个密码Reactjs 要修复此问题,请取消useEffect清理函数中的所有订阅和异步任务,reactjs,Reactjs,我有这个密码 import ReactDOM from "react-dom"; import React, { useState, useEffect } from "react"; import { BrowserRouter as Router, Route, Link } from "react-router-dom"; function ParamsExample() { return ( <Router&
import ReactDOM from "react-dom";
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
function ParamsExample() {
return (
<Router>
<div>
<h2>Accounts</h2>
<Link to="/">Netflix</Link>
<Route path="/" component={Miliko} />
</div>
</Router>
);
}
const Miliko = ({ match }) => {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
(async function() {
setIsError(false);
setIsLoading(true);
try {
const Res = await fetch("https://foo0022.firebaseio.com/New.json");
const ResObj = await Res.json();
const ResArr = await Object.values(ResObj).flat();
setData(ResArr);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
})();
console.log(data);
}, [match]);
return <div>{`${isLoading}${isError}`}</div>;
};
function App() {
return (
<div className="App">
<ParamsExample />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
从“react-dom”导入ReactDOM;
从“React”导入React,{useState,useffect};
从“react Router dom”导入{BrowserRouter as Router,Route,Link};
函数ParamsExample(){
返回(
账户
网飞公司
);
}
const Miliko=({match})=>{
const[data,setData]=useState([]);
const[isLoading,setIsLoading]=useState(false);
const[isError,setIsError]=useState(false);
useffect(()=>{
(异步函数(){
setIsError(假);
设置加载(真);
试一试{
const Res=等待获取(“https://foo0022.firebaseio.com/New.json");
const ResObj=await Res.json();
const ResArr=await Object.values(ResObj.flat();
setData(ResArr);
}捕获(错误){
setIsError(真);
}
设置加载(假);
})();
控制台日志(数据);
},[匹配];
返回{${isLoading}${isError}`};
};
函数App(){
返回(
);
}
const rootElement=document.getElementById(“根”);
render(,rootElement);
我创建了三个链接来打开Miliko
组件。但当我快速点击链接时,我会出现以下错误:
若要修复,请取消useEffect中的所有订阅和异步任务
清理功能
fetchData
是一个异步函数,它将返回一个承诺。但是你调用了它却没有解决它。如果需要在卸载组件时执行任何清理,请在包含清理代码的效果中返回一个函数。试试这个:
const Miliko = () => {
const [data, setData] = useState({ hits: [] });
const [url, setUrl] = useState('http://hn.algolia.com/api/v1/search?query=redux');
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
(async function() {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
})();
return function() {
/**
* Add cleanup code here
*/
};
}, [url]);
return [{ data, isLoading, isError }, setUrl];
};
我建议阅读官方文件,其中有详细的解释,还有一些更可配置的参数。我认为问题是在异步调用完成之前卸载造成的
constuseasync=()=>{
const[data,setData]=useState(null)
常量mountedRef=useRef(真)
const execute=useCallback(()=>{
设置加载(真)
返回asyncFunc()
。然后(res=>{
如果(!mountedRef.current)返回null
设置数据(res)
返回res
})
}, [])
useffect(()=>{
return()=>{
mountedRef.current=false
}
}, [])
}
mountedRef
用于指示组件是否仍在安装。如果是,则继续异步调用以更新组件状态,否则,跳过它们
这应该是不出现内存泄漏(访问清理内存)问题的主要原因
更新
以上答案引出了我们在团队中使用的以下组件
/**
* A hook to fetch async data.
* @class useAsync
* @borrows useAsyncObject
* @param {object} _ props
* @param {async} _.asyncFunc Promise like async function
* @param {bool} _.immediate=false Invoke the function immediately
* @param {object} _.funcParams Function initial parameters
* @param {object} _.initialData Initial data
* @returns {useAsyncObject} Async object
* @example
* const { execute, loading, data, error } = useAync({
* asyncFunc: async () => { return 'data' },
* immediate: false,
* funcParams: { data: '1' },
* initialData: 'Hello'
* })
*/
const useAsync = (props = initialProps) => {
const {
asyncFunc, immediate, funcParams, initialData
} = {
...initialProps,
...props
}
const [loading, setLoading] = useState(immediate)
const [data, setData] = useState(initialData)
const [error, setError] = useState(null)
const mountedRef = useRef(true)
const execute = useCallback(params => {
setLoading(true)
return asyncFunc({ ...funcParams, ...params })
.then(res => {
if (!mountedRef.current) return null
setData(res)
setError(null)
setLoading(false)
return res
})
.catch(err => {
if (!mountedRef.current) return null
setError(err)
setLoading(false)
throw err
})
}, [asyncFunc, funcParams])
useEffect(() => {
if (immediate) {
execute(funcParams)
}
return () => {
mountedRef.current = false
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return {
execute,
loading,
data,
error
}
}
即使组件已卸载,useEffect也会尝试与数据获取过程保持通信。由于这是一种反模式,并且会导致应用程序内存泄漏,因此取消订阅useEffect会优化应用程序
在下面的简单实现示例中,您将使用标志(isSubscribed)来确定何时取消订阅。在效果结束时,你会打电话进行清理
export const useUserData=()=>{
常量初始状态={
用户:{},
错误:null
}
常量[状态,设置状态]=使用状态(初始状态);
useffect(()=>{
//清理控制器
让isSubscribed=true;
//尝试与服务器API通信
获取(服务器URI)
.then(response=>response.json())
.然后(数据=>IssuSubscribed?设置状态(prevState=>({
…状态,用户:数据
})):null)
.catch(错误=>{
如果(已订阅){
设置状态(prevState=>({
…国家,
错误
}));
}
})
//取消订阅以生效
return()=>(isSubscribed=false)
}, []);
返回状态
}
如果没有@windmaomao的回答,你可以从这个博客上读到更多的内容,我可以花上几个小时来想办法取消订阅 简而言之,我分别使用了两个钩子
useCallback
来记忆函数和useffect
来获取数据
const fetchSpecificItem=useCallback(异步({itemId})=>{
试一试{
…获取数据
/*
设置状态之前,请确保已安装部件
否则,返回null,不允许卸载组件。
*/
如果(!mountedRef.current)返回空值;
/*
如果安装了部件,请随意设置状态
*/
}捕获(错误){
…处理错误
}
},[mountedRef])//将变量添加为依赖项
我使用useffect
获取数据
我不能调用函数的内部效果,因为不能在函数内部调用钩子
useffect(()=>{
获取特定项目(输入);
return()=>{
mountedRef.current=false;//清除函数
};
},[input,fetchSpecificItem]);//将函数添加为依赖项
谢谢大家,你们的贡献帮助我更多地了解了钩子的用法。我的案例与这个问题的目的大不相同。但我还是犯了同样的错误 我的情况是因为我有一个“列表”,它是使用数组中的
.map
呈现的。我需要使用.shift
。(删除数组中的第一项)
如果数组只有一个项目,这是正常的,但由于它有两个项目->第一个项目被“删除/移位”,并且因为我使用了键={index}
(而索引来自.map
),它假设第二个项目(后来是第一个)与移位项目是同一个组件
React保留了来自第一个项目的信息(它们都是节点),因此,如果第二个节点使用了useffect()
,React抛出错误,说明该组件已经卸载,因为索引为0且键为0的前一个节点与第二个组件具有相同的键0
第二个组件正确使用了useffect
,但假设,