Добавление тега скрипта в React/JSX

у меня есть относительно простая проблема с попыткой добавить встроенные скрипты в компонент 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, у меня есть реальный идентификатор, который я не хотел делиться.

9 ответов


вы хотите получать и выполнять сценарий снова и снова, каждый раз, когда этот компонент отображается, или только один раз, когда этот компонент монтируется в DOM?

возможно, попробовать что-то вроде этого:

    componentDidMount () {
        const script = document.createElement("script");

        script.src = "https://use.typekit.net/foobar.js";
        script.async = true;

        document.body.appendChild(script);
    }

однако это действительно полезно, только если скрипт, который вы хотите загрузить, недоступен в качестве модуля / пакета. Во-первых, я всегда:

  • ищите пакет на npm
  • Загрузите и установите пакет в моем проекте (npm install typekit)
  • import пакет, где мне это нужно (import Typekit from 'typekit';)

это, вероятно, как вы установили пакеты react и react-document-title из вашего примера, и есть пакет Typekit доступен на npm.


далее к ответам выше вы можете сделать это:

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 привязан к this и скрипт вводится в него.

демо можно найти на codesandbox.io


мой любимый способ - использовать React Helmet-это компонент, который позволяет легко манипулировать головой документа так, как вы, вероятно, уже привыкли.

например

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


ответ Алекс Макмиллан предоставленный помог мне больше всего, но не совсем работал для более сложного тега скрипта.

Я слегка подправил его ответ, чтобы придумать решение для длинного тега с различными функциями, который дополнительно уже устанавливал "src".

(для моего случая использования скрипт, необходимый для жизни в голове, который также отражен здесь):

  componentWillMount () {
      const script = document.createElement("script");

      const scriptText = document.createTextNode("complex script with functions i.e. everything that would go inside the script tags");

      script.appendChild(scriptText);
      document.head.appendChild(script);
  }

Я создал компонент React для этого конкретного случая:https://github.com/coreyleelarson/react-typekit

просто нужно передать свой идентификатор набора Typekit в качестве опоры, и вам хорошо идти.

import React from 'react';
import Typekit from 'react-typekit';

const HtmlLayout = () => (
  <html>
    <body>
      <h1>My Example React Component</h1>
      <Typekit kitId="abc123" />
    </body>
  </html>
);

export default HtmlLayout;

Если вам нужно иметь <script> блок в SSR (рендеринг на стороне сервера), подход с componentDidMount не будет работать.

можно использовать react-safe библиотека. Код в React будет:

import Safe from "react-safe"

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

существует очень хороший обходной путь с помощью Range.createContextualFragment.

/**
 * Like React's dangerouslySetInnerHTML, but also with JS evaluation.
 * Usage:
 *   <div ref={setDangerousHtml.bind(null, html)}/>
 */
function setDangerousHtml(html, el) {
    if(el === null) return;
    const range = document.createRange();
    range.selectNodeContents(el);
    range.deleteContents();
    el.appendChild(range.createContextualFragment(html));
}

это работает для произвольного HTML, а также сохраняет контекстную информацию, такую как document.currentScript.


можно использовать npm postscribe для загрузки скрипта в react component

postscribe('#mydiv', '<script src="https://use.typekit.net/foobar.js"></script>')

решение зависит от сценария. Как и в моем случае, мне пришлось загрузить calendly embed внутри компонента react.

Calendly ищет div и читает из него data-url атрибут и загружает iframe внутри указанного div.

это все хорошо, когда вы сначала загрузить страницу: во-первых, div с data-url просмотру. Затем в тело добавляется скрипт calendly. Браузер загружает и оценивает его, и мы все идем домой счастливыми.

проблема возникает, когда вы перемещаетесь и затем вернитесь на страницу. На этот раз скрипт все еще находится в теле, и браузер не повторно загружает и не оценивает его.

исправления:

  1. On componentWillUnmount найдите и удалите элемент script. Затем на повторном монтировании повторите описанные выше шаги.
  2. Enter $.getScript. Это отличный помощник jquery, который принимает URI сценария и обратный вызов успеха. После загрузки сценария он оценивает его и запускает обратный вызов успеха. Все, что мне нужно сделать, это в моем componentDidMount $.getScript(url). Мой render метод уже имеет Calendly div. И это работает гладко.