reactjs - Hvordan unngå uendelige oppdateringer i useEffect, når avhengigheten er en funksjon
En hook sjekker om det finnes etterligningsinformasjon lagret i lagring når siden gjengis, og i så fall setter den denne informasjonen i den globale AppState-konteksten.
const impersonateStorageKey = `impersonation_${config.environment}`
// signature const impersonate: (empScope: number) => void
const { currentImpersonation, impersonate } = useAppContext()
useEffect(() => {
if (!window || !window.localStorage) return;
const storageString = localStorage.getItem(impersonateStorageKey)
if (!storageString) return;
const data = JSON.parse(storageString)
impersonate(data.currentImpersonation)
}, [impersonateStorageKey])
Med en andre krok, som vedvarer en endring i den nåværende etterlignede identiteten til lagringen:
useEffect(() => {
if (!window || !window.localStorage) return;
localStorage.setItem(impersonateStorageKey, JSON.stringify({ /* ... */}))
}, [currentImpersonation, impersonateStorageKey])
Pluss de relevante delene fra useAppContext
const useAppContext = () => {
const { state, dispatch } = useContext(AppContext)
if (!state) {
throw new Error("useAppContext must be used within within AppContextProvider")
}
const impersonate = (employeeScope: string | number) => dispatch({ type: 'IMPERSONATE', value: employeeScope })
const currentImpersonation = state.currentImpersonation
return {
impersonate,
currentImpersonation,
}
}
Dette fungerer som det skal, men linteren klager over at avhengigheten impersonate mangler fra den første useEffect-hooken
Når jeg legger til impersonate i avhengighetslisten, vil dette forårsake en konstant oppdateringssløyfe og gjøre applikasjonen uresponsiv.
Jeg vet hva som forårsaker denne oppførselen, men jeg klarer ikke å se en løsning (bortsett fra å ignorere regelen) på hvordan man kan bryte løkken og gjøre linten fornøyd.
Hvilke tilnærminger kan jeg ta her?
Svar #1
Du kan memoize funksjonen når du oppretter den med useCallback:
const useAppContext = () => {
const { state, dispatch } = useContext(AppContext)
const impersonate = useCallback(
(employeeScope: string | number) => dispatch({
type: 'IMPERSONATE',
value: employeeScope
}), [dispatch])
if (!state) {
throw new Error("useAppContext must be used within within AppContextProvider")
}
const currentImpersonation = state.currentImpersonation
return {
impersonate,
currentImpersonation,
}
}
Hvis du ikke kan, vil en løsning være å legge funksjonen i en ref. ref er en uforanderlig referanse til et objekt, med current-egenskapen som er foranderlig. Siden selve ref er uforanderlig, kan du bruke den som en avhengighet uten å aktivere useEffect (linteren vet det, og du trenger ikke engang å angi det som en avhengighet). Du kan endre current så mye du vil.
const impersonateRef = useRef(impersonate)
useEffect(() => {
impersonateRef.current = impersonate
}, [impersonate])
useEffect(() => {
if (!window || !window.localStorage) return
const storageString = localStorage.getItem(impersonateStorageKey)
if (!storageString) return
const data = JSON.parse(storageString)
impersonateRef.current(data.currentImpersonation)
}, [impersonateStorageKey])
