skip to Main Content

I have a custom hook that returns a value by parameters that it gets. I need to recreate this with any updated parameter so I need to do that inside useEffect but I can’t call the hook inside useEffect. How could I implement it?

The custom hook:

useApi.tsx

export default function useApi(id) {
  const api = useGlobalApi()
  const user = api.get(`..../${id}`)

  ...

  return user
}

Api.tsx

export default function Api(id) {
  const [user, setUser] = useState({})
  const data = useApi(id)

  useEffect(() => {
    const user = useApi(id)
    setUser(user)
  }, [id])



  ...
}

I’m getting this error:

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app`

How can I resolve this and get the expected result?

3

Answers


  1. Return a memoized function (wrapped with useCallback) from the hook, and use that function in the useEffect.

    Custom hook:

    export default function useApi(id) {
      const api = useGlobalApi()
      
      return () => useCallback(() => api.get(`..../${id}`), [id])
    }
    

    Usage:

    const getUser = useApi(id)
    
    useEffect(() => {
      const callApi = async () => {
        const user = await getUser()
        setUser(user)
      }
    
      callApi()
    }, [getUser])
    

    You can also get an immutable function, and pass the id in the useEffect:

    export default function useApi() {
      const api = useGlobalApi()
      
      return () => useCallback(id => api.get(`..../${id}`), [])
    }
    

    Usage:

    const getUser = useApi()
    
    useEffect(() => {
      const callApi = async () => {
        const user = await getUser(id)
        setUser(user)
      }
    
      callApi()
    }, [getUser, id])
    
    Login or Signup to reply.
  2. export default function useApi(id) {
        const [user,setUser]=useState(null)
        // maybe use a different name for useApi here, current hook name is `useApi` 
        // return value is usually set inside useEffect. 
        const api = useGlobalApi()
    
        useEffect(()=>{
      
        const userRes = api.get(`..../${id}`)
        setUser(userRes)
        },[])
        return user
      }
    

    then inside Api.tsx

    export default function Api(id) {
        const user=useApi()
        ...
      }
    
    Login or Signup to reply.
  3. Your question is a bit confused because you are using 2 hooks called ‘useApi’, but I’ll try my best to help.

    Since you are using a hook, you don’t need to call is again on useEffect.

    Api.tsx

    export default function Api(id) {
      const [user, setUser] = useState({})
      const usr = useApiCustom(id)
    
      useEffect(() => {
        setUser(usr)
      }, [usr])
    
      ...
    }
    

    useApiCustom.tsx

    export default function useApiCustom(id) {
      const api = useApi()
      const user = api.get(`..../${id}`)
    
      return user
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search