Custom hooks not working properly with useEffect

Viewed 2201

I wasn't sure on a title for this issue, but I can better explain it here. I have a custom hook that is relying on some information. Part of the information must rely on an async call.

I have three situations happening.

  1. Tried conditionally rendering the custom hook, but react does not like that due to rendering more hooks on a different render.
  2. The custom hook is only mounting once and not passing in the updated information it needs.
  3. I tried passing the dependency to the custom hook and it causes an infinite loop.

Here is a small example of what I'm doing.

Custom Hook:

export function useProducts(options){ 
  const [products, setProducts] = useContext(MyContext)

  useEffect(() => {
    // only gets called once with `options.asyncValue` === null
    // needs to be recalled once the new value is passed in
    const loadProducts = async () => {
      const data = await asyncProductReq(options)
      setProducts(data)
    }
    loadProducts()
  }, []) // if I pass options here it causes the infinite loop

  return [products, setProducts]
}

Inside function calling:

export function(props){
 const [asyncValue, setValue] = useState(null)

 useEffect(() => {
   const loadValue = async () => { 
     const data = await asyncFunc()
     setValue(data)
   }
   loadValue()
 }, []}

 const options = {...staticValues, asyncValue}
 const [products] = useProducts(options)
 return (
  <h2>Hello</h2>
 )
}

I know that I need to pass the options to be a dependency, but I can't figure out why it's causing an infinite reload if the object isn't changing once the async call has been made inside the func.

1 Answers

You were correct in adding options in the dependencies list for your custom hook.

The reason it is infinitely looping is because options IS constantly changing.

The problem is you need to take it one step further in the implementation and make use of the useMemo hook so options only changes when the async value changes, instead of the whole component changing.

So do this:

const options = React.useMemo(() => ({...staticValues, asyncValue}), [asyncValue])
Related