React Hook useEffect has a missing dependency: 'dispatch'

前端 未结 2 1122
既然无缘
既然无缘 2021-02-07 05:27

This is my first time working with react js , im trying to remove the alert when leaving this view cause i don\'t want to show it on the other view but in case that there is no

2条回答
  •  故里飘歌
    2021-02-07 06:18

    dispatch comes from a custom hook so it doesn't have an stable signature therefore will change on each render (reference equality). Add an aditional layer of dependencies by wrapping the handler inside an useCallback hook

       const [foo, dispatch] = myCustomHook()
      
       const stableDispatch = useCallback(dispatch, []) //assuming that it doesn't need to change
    
       useEffect(() =>{
            stableDispatch(foo)
       },[stableDispatch])
    

    useCallback and useMemo are helper hooks with the main purpose off adding an extra layer of dependency check to ensure synchronicity. Usually you want to work with useCallback to ensure a stable signature to a prop that you know how will change and React doesn't.

    A function(reference type) passed via props for example

    const Component = ({ setParentState }) =>{
        useEffect(() => setParentState('mounted'), [])
    }
    

    Lets assume you have a child component which uppon mounting must set some state in the parent (not usual), the above code will generate a warning of undeclared dependency in useEffect, so let's declare setParentState as a dependency to be checked by React

    const Component = ({ setParentState }) =>{
        useEffect(() => setParentState('mounted'), [setParentState])
    }
    

    Now this effect runs on each render, not only on mounting, but on each update. This happens because setParentState is a function which is recreated every time the function Component gets called. You know that setParentState won't change it's signature overtime so it's safe to tell React that. By wrapping the original helper inside an useCallback you're doing exactly that (adding another dependency check layer).

    const Component = ({ setParentState }) =>{
       const stableSetter = useCallback(() => setParentState(), [])
    
       useEffect(() => setParentState('mounted'), [stableSetter])
    }
    

    There you go. Now React knows that stableSetter won't change it's signature inside the lifecycle therefore the effect do not need too run unecessarily.

    On a side note useCallback it's also used like useMemo, to optmize expensive function calls (memoization).

    The two mai/n purposes of useCallback are

    • Optimize child components that rely on reference equality to prevent unnecessary renders. Font

    • Memoize expensive calculations

    UPDATE 09/11/2020

    This solution no longer works on es-lint-plugin-react-hooks@4.1.0 and above, actually it doesn't need to work anymore. Now useMemo and useCallback can safely receive referential types as dependencies.#19590

    function MyComponent() {
      const foo = ['a', 'b', c']; // <== This array is reconstructed each render
      const normalizedFoo = useMemo(() => foo.map(expensiveMapper), [foo]);
      return 
    }
    

    Here is another example of how to safely stabilize(normalize) a callback

    const Parent = () => {
        const [message, setMessage] = useState('Greetings!')
    
        return (
            

    { message }

    ) } const Child = ({ setter }) => { const stableSetter = useCallback(args => { console.log('Only firing on mount!') return setter(args) }, [setter]) useEffect(() => { stableSetter('Greetings from child\'s mount cycle') }, [stableSetter]) //now shut up eslint const [count, setCount] = useState(0) const add = () => setCount(c => c + 1) return ( ) }

提交回复
热议问题