score:0

In your case there is no side-effect, it's just data derived from state, so you should use different tools instead of useEffect.

First filter could be implemented with just getting value by key:

const carsByBrand = allCars[brand];

Complexity of this operation is constant, so memoization will be too expensive.

And second filter is debounced, but you might want to memoize it because of filtering with linear complexity. For these purposes you can use for example useDebouncedMemo hook:

const filteredCars = useDebouncedMemo(() => {
   return carsByBrand.filter(model => model.startsWith(filterKey)
}, [filterKey, carsByBrand], 500)

In this case dependencies of hook are exhaustive and logic is much more declarative.

score:2

Yes, you should follow this rule all the time, when you find your code break with following it, that means good practice is not followed. That is the meaning of this rule, make sure you design code well.


I imagine in your case the code looks like this:

  const Test = () => {
   const [wantToSync] = useState(0)
   const [notWantToSync] = useState(0) // notWantToSync also might come from props, i'll use state as example here
   useEffect(() => {
    fetch('google.com', {
      body: JSON.stringify({wantToSync, notWantToSync})
    }).then(result => {
      // ...
    })
   }, [wantToSync]) // component is supposed to be reactive to notWantToSync, missing notWantToSync in dep is dangerous
 }

If notWantToSync was defined as a state of the component, the component is supposed to be reactive to it, including useEffect. If that is not what you want, notWantToSync shouldn't be state from start.

const Test = () => {
  const [wantToSync] = useState(0)
  const notWantToSyncRef = useRef(0) // hey I don't want notWantToSync to be reactive, so i put it in useRef
  useEffect(() => {
    fetch('google.com', {
      body: JSON.stringify({wantToSync, notWantToSync: notWantToSyncRef.current})
    }).then(result => {
      // ...
    })
  }, [wantToSync, notWantToSyncRef]) // yeah, now eslint doesn't bother me, and notWantToSync doesn't trigger useEffect anymore
}

Normally you don't need to do if else in useEffect to stop re-rendering, there're other approaches like useMemo or useCallback to do similar things in addition to useRef although they have different using context.


I see your struggle in the new example, so you want to do a throttle, and if filterKey is added to the first useEffect dep, the throttle will be broken. My opinion is that when you find yourself in a situation like this, that often means there's better practice(eslint exhaustive help to identify it), for example, extract throttle logic to a hook: https://github.com/streamich/react-use/blob/master/src/useThrottle.ts.

Exhaustive deps is not something vital to follow, it's just a good practice to make sure your code is designed well. The above example proves it that


Related Query

More Query from same tag