ReactJS - 从外部库打开上传对话框

IT技术 javascript reactjs dom
2021-05-05 07:29:03

这是我需要从外部库中获得的特定上传功能。

我想要实现的是:如果我单击由我的 ReactJS 应用程序呈现的按钮,则打开此外部上传对话框。

我有几个想法:

  1. 解决方案:如果我单击我的 ReactJS 按钮,则在“BS_WIDGET_INITIATOR”元素上执行合成单击事件(试过了,没有用)
  2. 解决方案:使用 ReactJS 克隆该元素并渲染它(丑陋)
  3. 解决方案:在 ReactJS 中构建等效的 HTML,然后执行脚本

我从解决方案 3 开始。

这是外部脚本的原始集成:

<script type="text/javascript" src="https://blabla.de/js/widget.js">
  BS.CONFIG = {
    "token": "546bc22e-d747-421f-b4bf-b19b5129816b",
    "hostname": "https://blabla.de/match",
    "redirectOnError": "ERROR_PAGE",
    "postOriginalDocument": true,
    "images": {
      "dropbox": "https://www.blabla.de/dropbox.svg",
      "googledrive": "https://www.blabla.de/googledrive.svg",
      "onedrive": "https://www.blabla.de/onedrive.svg",
      "cv": "https://www.blabla.de/cv.svg"
    },
    "postProfileUrl": "LANDING_PAGE",
    "gapiClientId": "GOOGLE_API_WEBCLIENT_ID",
    "oneDriveApiKey": "ONEDRIVE_APP_KEY"
  }
</script>

<button id="BS_WIDGET_INITIATOR">Apply now</button>
<div id="BS_WIDGET_CONTAINER" style="display:none">
  Apply with
  <hr/>
  <div class="BS_WIDGET" rel="dropbox"></div>
  <div class="BS_WIDGET" rel="googledrive"></div>
  <div class="BS_WIDGET" rel="onedrive"></div>
  <hr id="BS_WIDGET_HYBRID_SEPARATOR" />
  <div class="BS_WIDGET" rel="cv"></div>
</div>

<style type="text/css">
  #BS_WIDGET_CONTAINER {
    background-color: #fff;
    box-sizing: content-box;
    border: 1px solid #ccc;
    border-radius: 5px;
    width: 220px;
    text-align: center;
    color: #666;
    font-family: sans-serif;
    font-size: 12px;
  }
  
  .BS_WIDGET {
    display: inline-block;
    padding: 5px;
  }
  
  .BS_WIDGET[rel=cv],
  .BS_WIDGET[rel=form] {
    display: block;
    text-align: left;
  }
  
  hr {
    border: solid #ccc;
    border-width: 1px 0 0 0;
    margin: 5px 0;
  }
  
  .BS_WIDGET:hover {
    background-color: #ddd;
  }
  
  .BS_WIDGET img[src$=".svg"] {
    height: 32px;
    width: 32px;
  }
  
  .BS_WIDGET[rel=cv] img[src$=".svg"],
  .BS_WIDGET[rel=form] img[src$=".svg"] {
    width: auto;
  }
</style>

<script type="text/javascript" src="https://www.dropbox.com/static/api/2/dropins.js" id="dropboxjs" data-app-key="DROP-INS_API_KEY"></script>
<script type="text/javascript" src="https://apis.google.com/js/client.js?onload=onGapiLoad"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
  google.load('picker', '1');
</script>
<script type="text/javascript" src="https://js.live.net/v7.0/OneDrive.js" id="onedrive-js"></script>

这是我的 ReactJS 方法:

import { useEffect } from 'react';

const useScript = (url) => {
  useEffect(() => {
    const script = document.createElement('script');
    script.async = true;
    script.src = url;
    window.BS = {
      ...window.BS,
      CONFIG: {
            token: "546bc22e-d747-421f-b4bf-b19b5129816b",
            hostname: "https://blabla.de/match",
            redirectOnError: "ERROR_PAGE",
            postOriginalDocument: true,
            images: {
              dropbox: "https://www.blabla.de/dropbox.svg",
              googledrive: "https://www.blabla.de/googledrive.svg",
              onedrive: "https://www.blabla.de/onedrive.svg",
              cv: "https://www.blabla.de/cv.svg"
            },
            postProfileUrl: "LANDING_PAGE",
            gapiClientId: "GOOGLE_API_WEBCLIENT_ID",
            oneDriveApiKey: "ONEDRIVE_APP_KEY"
          }
      };
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    };
  }, [url]);
};

export default useScript;

import React from 'react';
import { Button } from 'antd';
import getTranslation from '../../utils/getTranslation';

import useScript from '../../utils/useScript';

const t = getTranslation;
const UploadButton = ({ uris, disabled }) => {
  if (uris && uris.BS) {
    useScript(uris.BS);
  }
  return (
    <>
      <div id="BS_WIDGET_CONTAINER" style={{ display: 'none' }}>
        Apply with
        <hr />
        <div className="BS_WIDGET" rel="dropbox" />
        <div className="BS_WIDGET" rel="googledrive" />
        <div className="BS_WIDGET" rel="onedrive" />
        <hr id="BS_WIDGET_HYBRID_SEPARATOR" />
        <div className="BS_WIDGET" rel="cv" />
      </div>
      <Button id="BS_WIDGET_INITIATOR" disabled={disabled} type="primary">{t('upload-cv')}</Button>
    </>
  );
};
export default UploadButton;

但似乎 BS.CONFIG 设置不正确。我的方式不好吗?我怎样才能实现它?

2个回答

您是否在使用 create-react-app,因为它甚至不应该编译。文档

React 依赖于调用 Hook 的顺序。 [...]这就是必须在组件的顶层调用 Hook 的原因。如果我们想有条件地运行一个效果,我们可以将该条件 放在我们的 Hook 中”

您需要将条件移动到 useEffect 钩子中。


稍微切线,自定义挂钩最适合需要在各种不同组件中运行的效果,因此您不必重复自己。(也许类似于表单验证?)这个特定的功能是将一个非常具体的脚本附加到文档中,我怀疑您需要多次执行此操作。您最好将 useEffect 直接移动到 UploadButton 组件中。不过也只是个人意见。

此外,React 渲染有时会变得有点混乱。当用户移动到卸载“上传”按钮的页面然后重新添加它并在重新安装按钮时重新获取脚本时,您真的想删除脚本吗?当页面上有两个 UploadButton 时会发生什么?脚本被调用两次,这是一个问题吗?在最初的集成中,我想相信它只会在文件中被调用一次,所以也许可以检查 useEffect 钩子中的脚本,如果它已经存在,就可以保释。但是,这也只是猜测,也许您打算照原样。

我同意第三个解决方案是最好的方法。至于脚本执行,您应该能够将确切的脚本标签放入您的index.html.