Reactjs axios期间更改路由导致useEffect挂钩内存泄漏
当我点击一个选项卡,数据表正在加载(在axios调用期间),并且在加载完成(axios返回数据)之前,我点击了一个链接,该链接将我带到同一网站内的另一条路线,就会发生此错误 代码如下:Reactjs axios期间更改路由导致useEffect挂钩内存泄漏,reactjs,react-hooks,Reactjs,React Hooks,当我点击一个选项卡,数据表正在加载(在axios调用期间),并且在加载完成(axios返回数据)之前,我点击了一个链接,该链接将我带到同一网站内的另一条路线,就会发生此错误 代码如下: useEffect(() => { if (reloadTable) { setReloadTable(false); // Forces this useEffect to be called once } else { let dataSelected = []; con
useEffect(() => {
if (reloadTable) {
setReloadTable(false); // Forces this useEffect to be called once
} else {
let dataSelected = [];
const options = {
keyName: process.env.REACT_APP_KEY_NAME,
keyValue: process.env.REACT_APP_KEY_VALUE
}
if(tab === "Instructors"){
setIsLoadingInstructorsTable(true);
axios.post(`https://example.com/school/api/Instructors/GetAllInstructors`, options)
.then(data => {
dataSelected = data.data.instructors.map(instructorData => {
return {id: instructorData.id, name: instructorData.name, assistants: instructorData.assistants, students: instructorData.students, hours: instructorData.hours, classes: instructorData.classes};
});
setTableInstructorData(dataSelected);
setInstructorDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
}
).catch(err => {
console.log('error:', err);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
}
);
}
if(tab === "Assistants"){
setIsLoadingAssistantsTable(true);
axios.post(`https://example.com/school/api/Assistants/GetAllAssistants`, options)
.then(data => {
dataSelected = data.data.assistants.map(assistantData => {
return {id: assistantData.id, name: assistantData.name, hours: assistantData.hours};
});
setTableAssistantsData(dataSelected);
setAssistantsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
}
).catch(err => {
console.log('error:', err);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
}
);
}
if(tab === "Students"){
setIsLoadingStudentsTable(true);
axios.post(`https://example.com/school/api/Students/GetAllStudents`, options)
.then(data => {
dataSelected = data.data.students.map(studentData => {
return {id: assistantData.id, name: assistantData.name, books: assistantData.books, classes: assistantData.classes};
});
setTableStudentsData(dataSelected);
setStudentsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingStudentsTable(false);
setSearchTerm("");
}
).catch(err => {
console.log('error:', err);
setIsLoadingStudentsTable(false);
setSearchTerm("");
}
);
}
}
}, [tab, reloadTable]);
下面是我看到的错误:
我试图按照错误提示查找“useEffect清理函数”,但找不到任何有用的方法来解决此代码的问题。有人有什么想法吗
编辑:正在尝试useRef() 在这里找到一个解决方案后> 我试着用这个代码实现它
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
const isMounted = useRef(null);
useEffect(() => {
if (reloadTable) {
setReloadTable(false); // Forces this useEffect to be called once
} else {
let dataSelected = [];
const options = {
keyName: process.env.REACT_APP_KEY_NAME,
keyValue: process.env.REACT_APP_KEY_VALUE
}
if(tab === "instructors"){
setIsLoadingInstructorsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Instructors/GetAllInstructors`, options)
.then(data => {
dataSelected = data.data.instructors.map(instructorData => {
return {id: instructorData.id, name: instructorData.name, assistants: instructorData.assistants, students: instructorData.students, hours: instructorData.hours, classes: instructorData.classes};
});
setTableInstructorData(dataSelected);
setInstructorDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
isMounted.current = false;
}
).catch(err => {
console.log('error:', err);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
isMounted.current = false;
}
).finally(() => {
if (isMounted.current) {
setIsLoadingInstructorsTable(false);
}
});
}
if(tab === "assistants"){
setIsLoadingAssistantsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Assistants/GetAllAssistants`, options)
.then(data => {
dataSelected = data.data.assistants.map(assistantData => {
return {id: assistantData.id, name: assistantData.name, hours: assistantData.hours};
});
setTableAssistantsData(dataSelected);
setAssistantsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
isMounted.current = false;
}
).catch(err => {
console.log('error:', err);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
isMounted.current = false;
}
).finally(() => {
if (isMounted.current) {
setIsLoadingAssistantsTable(false);
}
});
}
if(tab === "students"){
setIsLoadingStudentsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Students/GetAllStudents`, options)
.then(data => {
dataSelected = data.data.students.map(studentData => {
return {id: assistantData.id, name: assistantData.name, books: assistantData.books, classes: assistantData.classes};
});
setTableStudentsData(dataSelected);
setStudentsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingStudentsTable(false);
setSearchTerm("");
isMounted.current = false;
}
).catch(err => {
console.log('error:', err);
setIsLoadingStudentsTable(false);
setSearchTerm("");
isMounted.current = false;
}
).finally(() => {
if (isMounted.current) {
setIsLoadingStudentsTable(false);
}
});
}
}
}, [tab, reloadTable]);
但是错误仍然会发生您可以使用一个ref来指示在执行所有状态更新之前是否卸载了组件。然后,当组件卸载时,将其设置为
true
:
const mounted = useRef(true);
// this will set mounted to false when unmounting...
useEffect(() => () => { mounted.current = false; }, []);
useEffect(() => {
axios.get(/**/).then(data => {
if (mounted.current) {
// do the state updates
}
})
}, [/* some requirements*/]);
或者完全没有参考号:
useEffect(() => {
let mounted = true;
axios.get(/**/).then(data => {
if (mounted) {
// do the state updates
}
})
return () => mounted = false;
}, [/* some requirements*/]);
不同之处在于,在这种情况下,如果效果的任何依赖项发生更改,状态更新也将被取消。这甚至可以说是更好的解决方案。但这不是问题,因为另一个请求也将进入队列。您可以使用一个ref,指示在执行所有状态更新之前是否卸载了组件。然后,当组件卸载时,将其设置为
true
:
const mounted = useRef(true);
// this will set mounted to false when unmounting...
useEffect(() => () => { mounted.current = false; }, []);
useEffect(() => {
axios.get(/**/).then(data => {
if (mounted.current) {
// do the state updates
}
})
}, [/* some requirements*/]);
或者完全没有参考号:
useEffect(() => {
let mounted = true;
axios.get(/**/).then(data => {
if (mounted) {
// do the state updates
}
})
return () => mounted = false;
}, [/* some requirements*/]);
不同之处在于,在这种情况下,如果效果的任何依赖项发生更改,状态更新也将被取消。这甚至可以说是更好的解决方案。但这不是问题,因为另一个请求也将排队。如果您不想在卸载组件后呈现任何内容,则可以使用此代码示例来实现这一点 我已经创建了_IsMounted变量并将其设置为false。当调用useEffect时,这意味着安装了组件,所以它设置为true 当组件从屏幕上卸载时,react从useEffect调用返回方法,并将其设置为false。所以,当您尝试在没有安装组件的情况下更新状态时,不会导致崩溃 这不会取消订阅或异步代码,但不会影响应用程序或导致任何错误
let IS_MOUNTED = false; // define in global scope
useEffect(() => {
IS_MOUNTED = true;
thisIsFuntion();
return (() => {
IS_MOUNTED = false;
})
},[])
const thisIsFuntion = () => {
if(IS_MOUNTED)
{
// Update your state only if compoment is mounted
}
}
如果您不想在卸载组件后渲染任何内容,那么可以使用此代码示例来实现这一点 我已经创建了_IsMounted变量并将其设置为false。当调用useEffect时,这意味着安装了组件,所以它设置为true 当组件从屏幕上卸载时,react从useEffect调用返回方法,并将其设置为false。所以,当您尝试在没有安装组件的情况下更新状态时,不会导致崩溃 这不会取消订阅或异步代码,但不会影响应用程序或导致任何错误
let IS_MOUNTED = false; // define in global scope
useEffect(() => {
IS_MOUNTED = true;
thisIsFuntion();
return (() => {
IS_MOUNTED = false;
})
},[])
const thisIsFuntion = () => {
if(IS_MOUNTED)
{
// Update your state only if compoment is mounted
}
}
如果其他人遇到此问题,以下是解决方案代码
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
const isMounted = useRef(true);
useEffect(() => () => { isMounted.current = false; }, []);
useEffect(() => {
if (reloadTable) {
setReloadTable(false); // Forces this useEffect to be called once
} else {
let dataSelected = [];
const options = {
keyName: process.env.REACT_APP_KEY_NAME,
keyValue: process.env.REACT_APP_KEY_VALUE
}
if(tab === "instructors"){
setIsLoadingInstructorsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Instructors/GetAllInstructors`, options)
.then(data => {
if(isMounted.current){
dataSelected = data.data.instructors.map(instructorData => {
return {id: instructorData.id, name: instructorData.name, assistants: instructorData.assistants, students: instructorData.students, hours: instructorData.hours, classes: instructorData.classes};
});
setTableInstructorData(dataSelected);
setInstructorDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
isMounted.current = false;
}
}
).catch(err => {
console.log('error:', err);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
isMounted.current = false;
}
);
}
if(tab === "assistants"){
setIsLoadingAssistantsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Assistants/GetAllAssistants`, options)
.then(data => {
if(isMounted.current){
dataSelected = data.data.assistants.map(assistantData => {
return {id: assistantData.id, name: assistantData.name, hours: assistantData.hours};
});
setTableAssistantsData(dataSelected);
setAssistantsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
isMounted.current = false;
}
}
).catch(err => {
console.log('error:', err);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
isMounted.current = false;
}
);
}
if(tab === "students"){
setIsLoadingStudentsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Students/GetAllStudents`, options)
.then(data => {
if(isMounted.current){
dataSelected = data.data.students.map(studentData => {
return {id: assistantData.id, name: assistantData.name, books: assistantData.books, classes: assistantData.classes};
});
setTableStudentsData(dataSelected);
setStudentsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingStudentsTable(false);
setSearchTerm("");
isMounted.current = false;
}
}
).catch(err => {
console.log('error:', err);
setIsLoadingStudentsTable(false);
setSearchTerm("");
isMounted.current = false;
}
);
}
}
}, [tab, reloadTable]);
如果其他人遇到此问题,以下是解决方案代码
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
const isMounted = useRef(true);
useEffect(() => () => { isMounted.current = false; }, []);
useEffect(() => {
if (reloadTable) {
setReloadTable(false); // Forces this useEffect to be called once
} else {
let dataSelected = [];
const options = {
keyName: process.env.REACT_APP_KEY_NAME,
keyValue: process.env.REACT_APP_KEY_VALUE
}
if(tab === "instructors"){
setIsLoadingInstructorsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Instructors/GetAllInstructors`, options)
.then(data => {
if(isMounted.current){
dataSelected = data.data.instructors.map(instructorData => {
return {id: instructorData.id, name: instructorData.name, assistants: instructorData.assistants, students: instructorData.students, hours: instructorData.hours, classes: instructorData.classes};
});
setTableInstructorData(dataSelected);
setInstructorDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
isMounted.current = false;
}
}
).catch(err => {
console.log('error:', err);
setIsLoadingInstructorsTable(false);
setSearchTerm("");
isMounted.current = false;
}
);
}
if(tab === "assistants"){
setIsLoadingAssistantsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Assistants/GetAllAssistants`, options)
.then(data => {
if(isMounted.current){
dataSelected = data.data.assistants.map(assistantData => {
return {id: assistantData.id, name: assistantData.name, hours: assistantData.hours};
});
setTableAssistantsData(dataSelected);
setAssistantsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
isMounted.current = false;
}
}
).catch(err => {
console.log('error:', err);
setIsLoadingAssistantsTable(false);
setSearchTerm("");
isMounted.current = false;
}
);
}
if(tab === "students"){
setIsLoadingStudentsTable(true);
isMounted.current = true;
axios.post(`https://example.com/school/api/Students/GetAllStudents`, options)
.then(data => {
if(isMounted.current){
dataSelected = data.data.students.map(studentData => {
return {id: assistantData.id, name: assistantData.name, books: assistantData.books, classes: assistantData.classes};
});
setTableStudentsData(dataSelected);
setStudentsDisplayTableData(dataSelected);
setListOfBooks(data.data.books);
setListOfClasses(data.data.classes);
setListOfcurriculums(data.data.datacurriculums);
setListOfMeetings(data.data.);
setIsLoadingStudentsTable(false);
setSearchTerm("");
isMounted.current = false;
}
}
).catch(err => {
console.log('error:', err);
setIsLoadingStudentsTable(false);
setSearchTerm("");
isMounted.current = false;
}
);
}
}
}, [tab, reloadTable]);
您可以使用useEffect的析构函数回调以及useState标志
const [isMounted, setIsMounted] = useState(false)
// Inside your useEffect
useEffect(() => {
setIsMounted(true);
//Inside your axios call
axios.post(`https://example.com/school/api/Instructors/GetAllInstructors`, options)
.then(data => {
if (isMounted) {
...
...
}
});
// destructor callback
return () => {
setIsMounted(false);
}
});
您可以使用useEffect的析构函数回调以及useState标志
const [isMounted, setIsMounted] = useState(false)
// Inside your useEffect
useEffect(() => {
setIsMounted(true);
//Inside your axios call
axios.post(`https://example.com/school/api/Instructors/GetAllInstructors`, options)
.then(data => {
if (isMounted) {
...
...
}
});
// destructor callback
return () => {
setIsMounted(false);
}
});
这确实有效,我唯一需要添加的是在axios之前,设置
mounted.current=true
,并在承诺返回后的if语句末尾设置mounted.current=false
。如果我没有做这两件事,它会将加载设置为永久true,这样数据就不会填充。@FiddleFreak它应该已经通过传递初始值useRef(true)
设置为true
。您是否使用了两个独立的useffect
s?您也可以完全不使用ref。我更新了我的答案。请也检查一下。这发生在制表符切换期间,因为一旦axios调用第二次发生,该值为false,因此if语句中的所有内容都不会执行。@FiddleFreak,但这实际上不可能,因为它仅在卸载时设置为false
(不是在请求之后,因此两个独立的useffect
s)如果组件再次挂载,它将创建一个新的ref,该ref将再次为true
。但是没有ref的解决方案可能会更好。在选项卡之间切换时,组件不会卸载。只有在路由之间切换时,组件才会卸载。单击新选项卡会导致它加载该表的数据(讲师/助理/学生)。因此,一旦为一个选项卡/表加载数据,就无法为另一个选项卡/表加载数据,除非在进行axios调用之前将isMounted设置为true。请参阅我的参考解决方案代码答案。这肯定有效,我唯一需要添加的是在axios之前,设置mounted.current=true
,并在if语句末尾在承诺返回集mounted.current=false
之后。如果我没有做这两件事,它会将加载永久设置为true,这样数据就不会填充。@FiddleFreak它应该已经通过传递初始值useRef(true)设置为true
。您是否使用了两个独立的useffect
s?您也可以不使用完全的ref来完成。我用它更新了我的答案。请也检查一下。这发生在制表符切换过程中,因为一旦axios调用第二次发生,该值为false,因此if语句中的所有内容都将永远不会执行。@FiddleFreak但这会发生ALY不应该是可能的,因为它仅在卸载时设置为false
(不是在请求之后,因此两个单独的useffect
s)如果组件再次挂载,它将创建一个新的ref,该ref将再次为true
。但是没有ref的解决方案可能会更好。在选项卡之间切换时,组件不会卸载。只有在路由之间切换时,组件才会卸载。单击新选项卡会导致它加载该表的数据(讲师/助理/学生)所以