React / JSX에 스크립트 태그 추가


264

인라인 스크립팅을 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>

두 방법 모두 원하는 스크립트를 실행하지 않는 것 같습니다. 나는 그것이 누락 된 간단한 것 같아요. 아무도 도와 줄 수 있습니까?

추신 : foobar를 무시하고 실제로 공유하고 싶지 않은 실제 ID가 있습니다.


5
기본 페이지 HTML에 포함시키는 대신 React를 통해 이것을로드하려는 특별한 동기가 있습니까? 이것이 작동하더라도 구성 요소가 마운트 될 때마다 스크립트를 다시 삽입한다는 의미입니다.
— loganfsmyth

그 경우입니까? DOM diffing이 사실이 아니라고 가정했지만의 구현에 달려 있다고 인정합니다 DocumentTitle.
— loganfsmyth

8
올바른 @loganfsmyth, 다음 상태에도 스크립트가있는 경우 React는 다시 렌더링 할 때 스크립트를 다시로드하지 않습니다.
— Max

답변:


404

편집 : 상황이 빠르게 변하고 구식입니다-업데이트 참조


이 컴포넌트가 렌더링 될 때마다 또는이 컴포넌트가 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예제 를 설치 한 방법 일 가능성이 있으며 npm에서 사용 가능한 Typekit 패키지가 있습니다 .


최신 정보:

이제 고리가 useEffect생겼으므로 다음과 같이 사용하는 것이 더 좋습니다 .

useEffect(() => {
  const script = document.createElement('script');

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

  document.body.appendChild(script);

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

다음은 커스텀 훅 (예 :)에 대한 훌륭한 후보입니다 hooks/useScript.js.

import { useEffect } from 'react';

const useScript = url => {
  useEffect(() => {
    const script = document.createElement('script');

    script.src = url;
    script.async = true;

    document.body.appendChild(script);

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

export default useScript;

다음과 같이 사용할 수 있습니다.

import useScript from 'hooks/useScript';

const MyComponent = props => {
  useScript('https://use.typekit.net/foobar.js');

  // rest of your component
}

감사합니다. 나는 이것에 대해 생각하고 있었다. 이 구현을 진행했습니다. 그냥 추가 try{Typekit.load({ async: true });}catch(e){}후appendChild
— ArrayKnight

2
TypeKit의 "고급"구현이이 방법에 더 적합하다고 결정했습니다.
— ArrayKnight

10
이것은 작동합니다-스크립트를로드하지만 스크립트의 코드에 액세스하는 방법은 무엇입니까? 예를 들어, 스크립트 안에있는 함수를 호출하고 싶지만 스크립트가로드 된 구성 요소 안에서는 호출 할 수 없습니다.
— zero_cool

2
스크립트가 페이지에 추가되면 정상적으로 실행됩니다. 예를 들어,이 방법을 사용하여 CDN에서 jQuery를 다운로드 한 경우 componentDidMount함수가 스크립트를 다운로드하여 페이지에 추가 한 후 jQuery및 $객체를 전역 적으로 사용할 수 있습니다 (예 : on window).
— Alex McMillan

1
인증 스크립트를 사용하는 것과 비슷한 문제가 있었고 반응 App.js 위의 레이어를 루트의 html 파일에 포함하는 것이 더 낫습니다. 누구나 유용하다고 생각되는 경우. @loganfsmith가 언급했듯이 ...
— devssh

57

위의 답변 외에도 다음을 수행 할 수 있습니다.

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 에서 찾을 수 있습니다


5
this.instance는 저에게 효과적이지 않지만 document.body.appendChild는 Alex McMillan의 답변에서했습니다.
— 멋진 것

6
this.instance렌더 메소드 내부의 참조에 바인딩하지 않았을 것입니다 . 데모 링크를 추가하여 작동하는지 확인
— sidonaldson

1
@ShubhamKushwah 서버 측 렌더링을 수행해야합니까?
— ArrayKnight

@ArrayKnight yes 나중에 서버에 이러한 객체가 존재하지 않는다는 것을 알았습니다. document, window. 따라서 저는 npm global패키지를 선호 합니다
— Shubham Kushwah

s.async = true에 대한 필요성은 무엇인지, 그에 대한 참조를 찾을 수 없으며, 목적을 알기 위해 설명 할 수 있습니까?
— sasha romanov

50

제가 가장 좋아하는 방법은 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


5
이것이 지금까지 가장 좋은 솔루션입니다.
— paqash

4
불행히도, 그것은 작동하지 않습니다 ... 참조 codesandbox.io/s/l9qmrwxqzq
— Darkowic

2
@Darkowic, jQuery를 코드에 추가 async="true"한 <script>태그 에 추가하여 코드가 작동하도록했습니다 .
— Soma Mbadiwe

@SomaMbadiwe 왜 작동 async=true하지 않고 실패합니까?
— Webwoman

3
이것을 시도했지만 나를 위해 작동하지 않습니다. 제거 할 수없는 스크립트에 추가 속성을 주입한다는 유일한 이유로 react-helmet을 사용하지 않는 것이 좋습니다. 이것은 실제로 특정 스크립트를 깨뜨리고 관리자는 수년간 수정하지 않았 으며 github.com/nfl/react-helmet/issues/79
— Philberg

16

<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>

12

Alex Mcmillan이 제공 한 답변 은 가장 도움이되었지만 더 복잡한 스크립트 태그에는 효과가 없었습니다.

나는 이미 "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);
  }

7
인라인 JS를 페이지에 덤프하는 경우 왜 React를 사용하는지 이해가되지 않습니다 ...?
— Alex McMillan

2
document.head.removeChild(script);코드 를 추가 해야합니다. 그렇지 않으면 사용자가이 페이지를 방문하는 한 html에 무한한 수의 스크립트 태그를 만들 것입니다.
— sasha romanov

7

이 특정 사례에 대한 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;

3

를 사용하는 매우 좋은 해결 방법이 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.


사용 샘플을 사용하여 어떻게 작동하는지 예상 할 수 있습니까? 나를 위해 그것은 예를 들어 스크립트와 본문을 전달하는 작업하지 않습니다 ..
— Alex Efimov

3

npm postscribe반응 구성 요소에서 스크립트를로드 하는 데 사용할 수 있습니다.

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

1
내 문제를 해결
— RPichioli

1

반응 헬멧을 사용할 수도 있습니다

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>
    );
  }
};

헬멧은 일반 HTML 태그를 사용하여 일반 HTML 태그를 출력합니다. 매우 간단하고, React 초보자에게 친숙합니다.


0

여러 스크립트의 경우 이것을 사용하십시오

var loadScript = function(src) {
  var tag = document.createElement('script');
  tag.async = false;
  tag.src = src;
  document.getElementsByTagName('body').appendChild(tag);
}
loadScript('//cdnjs.com/some/library.js')
loadScript('//cdnjs.com/some/other/library.js')

0
componentDidMount() {
  const head = document.querySelector("head");
  const script = document.createElement("script");
  script.setAttribute(
    "src",
    "https://assets.calendly.com/assets/external/widget.js"
  );
  head.appendChild(script);
}

0

다음 링크에서 최상의 답변을 찾을 수 있습니다.

https://cleverbeagle.com/blog/articles/tutorial-how-to-load-third-party-scripts-dynamically-in-javascript

const loadDynamicScript = (callback) => {
const existingScript = document.getElementById('scriptId');

if (!existingScript) {
    const script = document.createElement('script');
    script.src = 'url'; // URL for the third-party library being loaded.
    script.id = 'libraryName'; // e.g., googleMaps or stripe
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }

  if (existingScript && callback) callback();
};

0

Alex McMillan 의 솔루션 에 따르면 다음과 같은 적응이 있습니다.
내 자신의 환경 : React 16.8+, next v9 +

// Script라는 커스텀 컴포넌트를 추가합니다.
// hooks / Script.js

import { useEffect } from 'react'

const useScript = (url, async) => {
  useEffect(() => {
    const script = document.createElement('script')

    script.src = url
    script.async = (typeof async === 'undefined' ? true : async )

    document.body.appendChild(script)

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

export default function Script({ src, async=true}) {

  useScript(src, async)

  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 src='http://maps.google.com/maps/api/js' async={false} />
</Fragment>

이 구성 요소에는 한 가지주의 사항이 있습니다.이 스크립트 구성 요소는 자체 형제의 순서 만 보장 할 수 있습니다. 동일한 페이지의 여러 구성 요소에서이 구성 요소를 여러 번 사용하면 스크립트 블록의 순서가 잘못되었을 수 있습니다. 그 이유는 모든 스크립트가 선언적으로 대신 document.body.appendChild에 의해 프로그래밍 방식으로 삽입 되었기 때문입니다. 헬멧은 헤드 태그에서 모든 스크립트 태그를 움직입니다.
— sully

@sully, 내 문제는 여기에 스크립트를 DOM에 여러 번 추가 한 것입니다. 지금까지 본 가장 좋은 해결책은 Component Unmounting 중에 DOM에서 자식 요소 (예 : <script>)를 제거하는 것입니다. 컴포넌트가 DOM에 마운트 될 때 다시 추가됨 (react-router-dom을 사용하고 있으며 하나의 컴포넌트 만 모든 컴포넌트의이 스크립트를 필요로 함)
— Eazy

0

파티에 조금 늦었지만 @Alex Macmillan 답변을보고 나 자신의 것을 만들기로 결정했으며 두 개의 추가 매개 변수를 전달했습니다. 비동기를 true / false로 설정하는 등의 스크립트를 배치 할 위치는 다음과 같습니다.

import { useEffect } from 'react';

const useScript = (url, position, async) => {
  useEffect(() => {
    const placement = document.querySelector(position);
    const script = document.createElement('script');

    script.src = url;
    script.async = typeof async === 'undefined' ? true : async;

    placement.appendChild(script);

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

export default useScript;

그것을 호출하는 방법은이 게시물의 허용 된 답변에 표시된 것과 정확히 동일하지만 두 가지 추가 매개 변수가 있습니다.

// First string is your URL
// Second string can be head or body
// Third parameter is true or false.
useScript("string", "string", bool);

0

반응 프로젝트에서 JS 파일을 가져 오려면이 명령을 사용하십시오.

  1. JS 파일을 프로젝트로 이동하십시오.
  2. 다음 명령을 사용하여 j를 페이지로 가져옵니다.

간단하고 쉽습니다.

import  '../assets/js/jquery.min';
import  '../assets/js/popper.min';
import  '../assets/js/bootstrap.min';

제 경우에는 이러한 JS 파일을 반응 프로젝트로 가져오고 싶습니다.


-3

솔루션은 시나리오에 따라 다릅니다. 필자의 경우와 마찬가지로 반응 구성 요소에 calendly embed를로드해야했습니다.

Calendly는 div를 찾아 해당 data-url속성 에서 읽고 해당 div 안에 iframe을로드합니다.

페이지를 처음로드 할 때 좋습니다. 먼저 div with data-url가 렌더링됩니다. 그런 다음 calendly 스크립트가 본문에 추가됩니다. 브라우저는 그것을 다운로드하고 평가하며 우리 모두는 행복합니다.

탐색 후 페이지로 돌아올 때 문제가 발생합니다. 이번에는 스크립트가 여전히 본문에 있으며 브라우저는 다시 다운로드 및 재평가하지 않습니다.

고치다:

  1. 에 componentWillUnmount찾아 스크립트 요소를 제거합니다. 그런 다음 다시 마운트 할 때 위 단계를 반복하십시오.
  2. 를 입력하십시오 $.getScript. 스크립트 URI와 성공 콜백을 취하는 멋진 jquery 도우미입니다. 스크립트가로드되면 스크립트가 평가되고 성공 콜백이 실행됩니다. 내가해야 할 일은 내 안에 componentDidMount $.getScript(url)있습니다. 내 render방법에는 이미 calendly div가 있습니다. 그리고 그것은 매끄럽게 작동합니다.

2
이 작업을 수행하기 위해 jQuery를 추가하는 것은 나쁜 생각이며 귀하의 경우는 귀하에게 매우 다릅니다. 실제로 API에 재 감지 호출이 있다고 확신하므로 Calendly 스크립트를 한 번 추가해도 아무런 문제가 없습니다. 스크립트를 반복해서 제거하고 추가하는 것은 올바르지 않습니다.
— sidonaldson

@sidonaldson jQuery는 프로젝트가 다른 프레임 워크 (및 libs)로 구성된 아키텍처를 단순히 반응하지 않고 유지 보수해야하는 경우 나쁜 습관이 아닙니다. 그렇지 않으면 네이티브 js를 사용하여 컴포넌트에 도달해야합니다.
— AlexNikonov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.