收到此错误“无效的钩子调用。只能在函数组件的主体内部调用钩子。”在反应功能组件内?

时间:2021-03-31 09:08:26

标签: javascript reactjs typescript react-hooks recaptcha

我收到此错误“无效的钩子调用。只能在函数组件的主体内部调用钩子。”当我尝试运行我的函数时。我正在尝试运行一个生成 reCaptcha 令牌的脚本,脚本如下:

<块引用>

这个组件应该生成令牌(这是我得到的地方 错误)

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])
}
<块引用>

在另一个组件中,我调用先前的脚本组件 一个动作类型通过,但我收到反应钩子错误 提到(仅显示相关代码片段)。

import * as React from 'react'
import { RecaptchaGen } from '../../../components/_molecules/Recaptcha'

const onLoginClick = () => {
    RecaptchaGen({ actionType: 'login' })
  }

我认为问题在于我调用/使用导入组件的方式,但我找不到解决方案?

4 个答案:

答案 0 :(得分:1)

您在常规 useEffect 函数中使用了 js 钩子。

hooks 只能在有效的 React 组件中调用。 RepcaptchaGen 不是 React 组件,因为它不使用 React 组件特定的东西,例如返回一些 jsx

答案 1 :(得分:1)

名称 RecaptchaGen 表明它是构造函数或组件,但两者都不是。它是一个自定义钩子,所以应该以 use 开头,例如:useRecaptchaGen。

以下是您在组件中使用它的方法:

export const useRecaptchaGen = (action) => {
  if (!action) {
    return;
  }
  const { actionType }: RecaptchaProps = action;
  //....rest of your code
};

const Component = () => {
  const [action, setAction] = React.useState(false);
  const onLoginClick = () => {
    setAction({ actionType: 'login' });
  };
  //cannot conditionally call hooks
  useRecaptchaGen(action);
};

答案 2 :(得分:1)

这样做的原因是您没有在 RecaptchaGen 函数中返回任何代码。 JSX 返回,除其他外,是 React Functional 组件与常规 javascript 函数的区别,常规 javascript 函数不能使用钩子。如果要解决此问题,请添加 return 语句。

答案 3 :(得分:1)

我不熟悉你的具体用例,但一个常见的模式是将一些效果逻辑封装到一个由你的自定义钩子返回的函数中,然后任何普通的JS函数(回调与否)都可以调用那个功能。

  1. RecaptchaGen 重命名为 useRecaptchaGen,以便将其用作 React 钩子。

  2. 返回一个函数来基本上替换第二个 useEffect 回调。

    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 };
    }
    
  3. 在组件中使用新的钩子和解构 generateRecaptcha

    const { generateRecaptcha } = useRecaptchaGen({ actionType: 'login' });
    
  4. generateRecaptcha 处理程序中调用 onLoginClick

    const onLoginClick = () => {
      generateRecaptcha();
    }