Skip to content

Instantly share code, notes, and snippets.

@trae410
Last active June 4, 2021 22:33

Revisions

  1. trae410 revised this gist Jun 4, 2021. No changes.
  2. trae410 revised this gist Jun 4, 2021. No changes.
  3. trae410 revised this gist Jun 4, 2021. No changes.
  4. trae410 revised this gist Jun 4, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion UsersDocs.js
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@ import React, { useState, useEffect, useRef } from 'react'
    // 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 [docs, setDocs] = useState(null) // must be null initially
    const [isFetchingDocs, setIsFetchingDocs] = useState(false)

    const componentIsMountedRef = useRef(false)
  5. trae410 created this gist Jun 4, 2021.
    79 changes: 79 additions & 0 deletions UsersDocs.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    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