我有一个相对简单的问题,试图将内联脚本添加到React组件。到目前为止我有:

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>
                    
                    <script src="https://use.typekit.net/foobar.js"></script>
                    <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}></script>
                </article>
            </DocumentTitle>
        );
    }
};

我也试过:

<script src="https://use.typekit.net/foobar.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>

这两种方法似乎都不能执行所需的脚本。我猜我错过了一件简单的事。有人能帮忙吗?

PS:忽略foobar,我有一个真正的id实际上在使用,我不想分享。


当前回答

根据Alex McMillan的解决方案,我有如下的调整。 我自己的环境:React 16.8+,下一个v9+

//添加一个名为Script的自定义组件 / / / Script.js hook

import { useEffect } from 'react'


// react-helmet don't guarantee the scripts execution order
export default function Script(props) {

  // Ruels: alwasy use effect at the top level and from React Functions
  useEffect(() => {
    const script = document.createElement('script')

    // src, async, onload
    Object.assign(script, props)

    let { parent='body' } = props

    let parentNode = document.querySelector(parent)
    parentNode.appendChild(script)

    return () => {
      parentNode.removeChild(script)
    }
  } )

  return null  // Return null is necessary for the moment.
}

//使用自定义组件,只需导入它,并将旧的小写<script>标记替换为自定义驼色大小写<script>标记就足够了。 / / index.js

import Script from "../hooks/Script";
    
<Fragment>
  {/* Google Map */}
  <div ref={el => this.el = el} className="gmap"></div>

  {/* Old html script */}
  {/*<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>*/}

  {/* new custom Script component */}
  <Script async={false} type="text/javascript" src='http://maps.google.com/maps/api/js' />
</Fragment>

其他回答

我最近遇到了一个问题, 尝试了这里给出的多种解决方案,最终满足于iframe, 如果你想在特定的屏幕上集成一个js插件,Iframe似乎可以无缝地工作

<iframe id="xxx" title="xxx" width="xxx" height="xxx" frameBorder="value" allowTransparency srcDoc={` <!doctype html> <html> <head> <title>Chat bot</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> </head> <body style="width:100%"> <script type="text/javascript"> ...... </script> </body> </html> `} />

你也可以使用反应头盔

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <meta charSet="utf-8" />
                <title>My Title</title>
                <link rel="canonical" href="http://example.com/example" />
                <script src="/path/to/resource.js" type="text/javascript" />
            </Helmet>
            ...
        </div>
    );
  }
};

Helmet接受普通HTML标记并输出普通HTML标记。这是非常简单的,React初学者友好。

根据Alex McMillan的解决方案,我有如下的调整。 我自己的环境:React 16.8+,下一个v9+

//添加一个名为Script的自定义组件 / / / Script.js hook

import { useEffect } from 'react'


// react-helmet don't guarantee the scripts execution order
export default function Script(props) {

  // Ruels: alwasy use effect at the top level and from React Functions
  useEffect(() => {
    const script = document.createElement('script')

    // src, async, onload
    Object.assign(script, props)

    let { parent='body' } = props

    let parentNode = document.querySelector(parent)
    parentNode.appendChild(script)

    return () => {
      parentNode.removeChild(script)
    }
  } )

  return null  // Return null is necessary for the moment.
}

//使用自定义组件,只需导入它,并将旧的小写<script>标记替换为自定义驼色大小写<script>标记就足够了。 / / index.js

import Script from "../hooks/Script";
    
<Fragment>
  {/* Google Map */}
  <div ref={el => this.el = el} className="gmap"></div>

  {/* Old html script */}
  {/*<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>*/}

  {/* new custom Script component */}
  <Script async={false} type="text/javascript" src='http://maps.google.com/maps/api/js' />
</Fragment>

我最喜欢的方法是使用React Helmet——这是一个组件,允许您以一种您可能已经习惯的方式轻松操作文档头。

e.g.

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <script src="https://use.typekit.net/foobar.js"></script>
                <script>try{Typekit.load({ async: true });}catch(e){}</script>
            </Helmet>
            ...
        </div>
    );
  }
};

https://github.com/nfl/react-helmet

除了上面的答案,你还可以这样做:

import React from 'react';

export default class Test extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    const s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.innerHTML = "document.write('This is output by document.write()!')";
    this.instance.appendChild(s);
  }

  render() {
    return <div ref={el => (this.instance = el)} />;
  }
}

div被绑定到此,脚本被注入其中。

演示可以在codesandbox.io上找到