I've a pair of functional component hooks on a page, the AddVocab
component adds new words to an array and ListVocab
maps the array.
Everything works fine until I navigate away from the page, upon which I get this error:
react_devtools_backend.js:3973 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 useEffect cleanup function.
To fix this memory leak, I've tried many variations of useEffect()
and cleanup function but to no avail. I know the cause is from the async VocabDataService
Axios call not unsubscribing/cancelling when the components are unmounted.
Can anyone provide the correct useEffect()
and cleanup function?
Here are the related files:
components/vocab/ListVocab.js
import React, { useState, useEffect } from "react";
import VocabDataService from "../../services/vocab";
export default function VocabList() {
const [vocab, setVocab] = useState([]);
const [currentVocab, setCurrentVocab] = useState(null);
const retrieveVocab = () => {
VocabDataService.getAll()
.then((response) => {
setVocab(response.data);
})
.catch((e) => {
console.log(e);
});
};
useEffect(() => retrieveVocab());
const removeAllVocab = () => {
VocabDataService.deleteAll()
.then((response) => {
setCurrentVocab(null);
})
.catch((e) => {
console.log(e);
});
};
return (
<div>
<div>
<button onClick={removeAllVocab}>Delete</button>
<ul>
{vocab.map((vocab, index) => (
<li key={index}>{vocab.word}</li>
))}
</ul>
</div>
</div>
);
}
components/vocab/AddVocab.js
import React, { useState } from "react";
import VocabDataService from "../../services/vocab";
export default function AddVocab() {
const [id, setId] = useState(null);
const [word, setWord] = useState("");
const [translation, setTranslation] = useState("");
const [starred, setStarred] = useState(false);
const [submitted, setSubmitted] = useState(false);
const onChangeWord = (e) => {
setWord(e.target.value);
};
const onChangeTranslation = (e) => {
setTranslation(e.target.value);
};
const saveVocab = () => {
var data = {
word: word,
translation: translation,
};
VocabDataService.create(data)
.then((response) => {
setId(response.data.id);
setWord(response.data.word);
setTranslation(response.data.translation);
setStarred(response.data.starred);
setSubmitted(true);
})
.catch((e) => {
console.log(e);
});
};
const newVocab = () => {
setId(null);
setWord("");
setTranslation("");
setStarred(false);
setSubmitted(false);
};
return (
<div className="submit-form">
{submitted ? (
<>{newVocab()}</>
) : (
<div>
<div className="form-group">
<label htmlFor="word">Word:</label>
<input
type="text"
className="form-control"
id="word"
required
value={word}
onChange={onChangeWord}
name="word"
/>
</div>
<div className="form-group">
<label htmlFor="translation">Translation:</label>
<input
type="text"
className="form-control"
id="translation"
required
value={translation}
onChange={onChangeTranslation}
name="translation"
/>
</div>
<button onClick={saveVocab} className="btn btn-success">
Submit
</button>
</div>
)}
</div>
);
}
pages/vocab.js
import React from "react";
import Routes from "../components/Routes";
import AddVocab from "../components/vocab/AddVocab";
import VocabList from "../components/vocab/VocabList";
export default function App() {
return (
<div>
<Routes />
<div>
<AddVocab />
<VocabList />
</div>
</div>
);
}
Thanks!
CodePudding user response:
useEffect has a cleanup function that sits in its return statement.
Try this:
`useEffect(() => {
retrieve vocabulary()
return removeAllVocab;
});`
React will execute the function when it's time to "clean". That is, when the component is to be unmounted.
Take a look at the React doc
I hope it works for you