Добавление тега сценария для 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 110

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 Kit ID в качестве опоры, и вы хорошо идти.

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 библиотека. Код в реагировать будет:

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

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 найти и удалить элемент сценария. Затем на повторном монтаже повторите описанные выше шаги.
  2. введите $.getScript. Это отличный помощник jquery, который принимает URI сценария и обратный вызов успеха. После загрузки скрипта он оценивает его и запускает обратный вызов успеха. Все, что мне нужно сделать, это в моем componentDidMount$.getScript(url). Мой render метод уже имеет calendly div. И это работает гладко.