使用 useEffect 加载脚本

Ask*_*Men 2 javascript reactjs use-effect

我正在使用 sdk,根据文档,我必须加载脚本:

 <body>
 <script>
    my sdk code
    my sdk code
    my sdk code
  </script>
  ..
  ..
  </body>
Run Code Online (Sandbox Code Playgroud)

你如何看到内部标签我有sdk代码。我正在使用 reactJs,我想在useEffect 钩子中加载这个脚本。
问题:sdk 的脚本如何运行,而不是在 body 标签中,而是在useEffect钩子中?

Emi*_*ier 9

您需要在 React 组件中动态添加脚本标签。最好的方法是使用 Context API。它将提供一个加载脚本并启动 FB SDK 的单点,并可以让每个孩子知道脚本何时加载以及 SDK 已准备好使用。

FbSdkScript.js

import React, { 
  createContext, 
  useContext, 
  useState, 
  useEffect 
} from 'react'

// Context.
export const FbSdkScriptContext = createContext()

// Create a custom hook to use the context.
export const useFbSdkScriptContext = () => useContext(FbSdkScriptContext)

// Provider of context.
const FbSdkScriptProvider = ({
  appId,
  autoLogAppEvents = true,
  xfbml = true,
  version = 'v8.0',
  children 
}) => {
  const [hasLoaded, setHasLoaded] = useState(false)
  const [isReady, setIsReady] = useState(false)

  /**
   * Extra security measure to check if the script has
   * already been included in the DOM
   */
  const scriptAlreadyExists = () => 
    document.querySelector('script#fb-sdk') !== null

  /**
   * Append the script to the document.
   * Whenever the script has been loaded it will
   * set the isLoaded state to true.
   */
  const appendSdkScript = () => {
    const script = document.createElement('script')
    script.id = 'fb-sdk'
    script.src = 'https://connect.facebook.net/en_US/sdk.js'
    script.async = true
    script.defer = true
    script.crossOrigin = 'anonymous'
    script.onload = () => setHasLoaded(true)
    document.body.append(script)
  };
  
  /**
   * Runs first time when component is mounted
   * and adds the script to the document.
   */
  useEffect(() => {
    if (!scriptAlreadyExists()) {
      appendSdkScript()
    }
  }, []);

  /**
   * Whenever the script has loaded initialize the
   * FB SDK with the init method. This will then set
   * the isReady state to true and passes that
   * through the context to the consumers.
   */
  useEffect(() => {
    if (hasLoaded === true) {
      FB.init({
        appId,
        autoLogAppEvents,
        xfbml,
        version 
      })
      setIsReady(true)
    }
  }, [hasLoaded])

  return (
    <FbSdkScriptContext.Provider value={{ isReady, hasLoaded }}>
      {children}
    </FbSdkScriptContext.Provider>
  )
}

export default FbSdkScriptProvider
Run Code Online (Sandbox Code Playgroud)

共享组件本身现在无需担心。它只需要知道何时加载脚本和初始化 FB SDK。它将通过isReadyContext API的状态获取该信号。

FbShareDialog.js

import React, { useEffect } from 'react'
import { useFbSdkScriptContext } from './FbSdkScript'

/**
 * This is the button that will trigger the dialog.
 * It uses the context created in the previous snippet to
 * know when the script has loaded and the API is ready
 * to use. 
 */
const FbShareDialog = ({ method = 'share', href }) => {
  const { isReady } = useFbSdkScriptContext()

  /**
   * Open share dialog when the button is clicked.
   * This will only be available when the isReady
   * state is true.
   */
  const handleClick = () => {
    FB.ui({
      method,
      href,
    }, response => {
      console.log(response)
    })
  }

  /**
   * If FB SDK is not yet ready, don't render the button.
   */
  if (!isReady) {
    return null
  }

  /**
   * Otherwise do render the button and set an onClick
   * event listener which triggers the dialog.
   */
  return (
    <button onClick={handleClick}>Share me</button>
  )
}

export default FbShareDialog
Run Code Online (Sandbox Code Playgroud)

这一切都集中在一个组件中,其中上下文提供程序是共享对话框按钮的父级。您只需要传递您的appId即可使 FbSdkScriptProvider组件正常工作。并添加href到按钮来告诉FB.ui什么 href 引用。

应用程序.js

import React from 'react'

import FbSdkScriptProvider from './FbSdkScript'
import FbShareDialog from './FbShareDialog'

const App = () => (
  <FbSdkScriptProvider appId={'your-app-id'}>
    <FbShareDialog href={'https://developers.facebook.com/docs/'} />
  <FbSdkScriptProvider/>
)
Run Code Online (Sandbox Code Playgroud)