I am receiving this error "Invalid hook call. Hooks can only be called inside of the body of a function component." when I try and run my function. I am trying to run a script that generates a reCaptcha token the script is as follows:
This component should generate the token(this is where I get the error)
import * as React from 'react'
import { RECAPTCHA_KEY } from '../../../utils/env'
// @ts-ignore
declare const window: any
interface RecaptchaProps {
actionType: string
}
export const RecaptchaGen = ({ actionType }: RecaptchaProps) => {
const siteKey = RECAPTCHA_KEY
const [loaded, setLoaded] = React.useState(false)
React.useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src = 'https://www.google.com/recaptcha/api.js?render=' + siteKey
document.appendChild(scriptTag)
return () => {
scriptTag.remove()
setLoaded(false)
}
}, [siteKey])
React.useEffect(() => {
if (!loaded) return
window.grecaptcha.ready(function () {
window.grecaptcha
.execute(siteKey, {
action: actionType,
})
.then(function (token: string) {
//reCaptch token generated needs to be sent to the back end for validation
console.log(token)
})
})
}, [loaded, actionType, siteKey])
}
Here within another component, I call the prior script component with an action type passed through but I get the react hook error mentioned(only showing relevant code snipets).
import * as React from 'react'
import { RecaptchaGen } from '../../../components/_molecules/Recaptcha'
const onLoginClick = () => {
RecaptchaGen({ actionType: 'login' })
}
I presume the issue is the way I am calling/using the imported component but I can't find a solution?
I am not familiar with your specific use case, but a common pattern is to encapsulate some of the effect logic into a function that is returned by your custom hook, then any normal JS function (callback or not) can invoke that function.
Rename RecaptchaGen
to useRecaptchaGen
so it can be used as a React hook.
Return a function to basically replace the second useEffect
callback.
export const useRecaptchaGen = ({ actionType }: RecaptchaProps) => {
const siteKey = RECAPTCHA_KEY
const [loaded, setLoaded] = React.useState(false)
React.useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src = 'https://www.google.com/recaptcha/api.js?render=' + siteKey
document.appendChild(scriptTag)
return () => {
scriptTag.remove()
setLoaded(false)
}
}, [siteKey]);
const generateRecaptcha = () => {
if (!loaded) return;
window.grecaptcha.ready(function () {
window.grecaptcha
.execute(siteKey, {
action: actionType,
})
.then(function (token: string) {
//reCaptch token generated needs to be sent to the back end for validation
console.log(token)
})
})
};
return { generateRecaptcha };
}
In the component use the new hook and destructure generateRecaptcha
.
const { generateRecaptcha } = useRecaptchaGen({ actionType: 'login' });
Invoke generateRecaptcha
in the onLoginClick
handler.
const onLoginClick = () => {
generateRecaptcha();
}