Skip to content

Instantly share code, notes, and snippets.

@trae410
Last active June 4, 2021 22:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trae410/2a8dbdb1ba38b60f3dc1503ab08fa146 to your computer and use it in GitHub Desktop.
Save trae410/2a8dbdb1ba38b60f3dc1503ab08fa146 to your computer and use it in GitHub Desktop.
Async get and set data in UseEffect while preventing memory leaks and eslint warnings
import React, { useState, useEffect, useRef } from 'react'
// This component would be conditionally rendered inside a parent component so that we can test the memory leak
// eg: if condition === 'a' render UsersDocs else render SomeOtherPage
// I havent tested this code but the real scenario has multiple different types of usersDocs to get
// and multiple different isFetchingDocs states
// the goal is to fetch and set the data only once with no memory leak errors and no eslint exhaustive or missing deps warnings
const UsersDocs = () => {
const [docs, setDocs] = useState(null) // must be null initially
const [isFetchingDocs, setIsFetchingDocs] = useState(false)
const componentIsMountedRef = useRef(false)
// some async function
const getDocs = async () => {
return [{name: "doc1"}, {name: "doc2"}]
}
// get and the docs and set them into state if the component is mounted
const handleGetDocs = async (setIsFetchingDocs, setDocs) => {
try {
setIsFetchingDocs(true)
const resDocs = await getDocs()
if (componentIsMountedRef.current) {
setDocs(docs => {
// compare previous state to resDocs and only set if different
// could use something like:
// if (!docs || (resDocs && (docs.length !== resDocs.length))) {
// or
// if (JSON.stringify(docs) !== JSON.stringify(resDocs)) {
if (!docs) {
return resDocs
} else return docs
})
setIsFetchingDocs(false)
}
} catch (err) {
console.log(err)
}
}
// track whether the component is mounted in a mutable variable
useEffect(() => {
componentIsMountedRef.current = true
return () => {
componentIsMountedRef.current = false
}
}, [])
// initiate getting and setting the docs
useEffect(() => {
if (!docs && !isFetchingDocs) {
handleGetDocs({setIsFetchingDocs, setDocs, getDocs})
}, [
//getDocs, // might be needed in the dependancy array if passed down in a prop or in context
setIsFetchingDocs,
setDocs
])
return (
<ul>
{
isFetchingDocs ? <li>...loading</li>
:
docs.map(doc => {
return <li>{doc.name}</li>
}
}
</ul>
)
}
export default App
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment