React / JSX'e komut dosyası etiketi ekleme


266

Bir React bileşenine satır içi komut dosyası eklemeye çalışırken nispeten basit bir sorun var. Şimdiye kadar ne var:

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

Ayrıca denedim:

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

Her iki yaklaşım da istenen komut dosyasını yürütmüyor gibi görünüyor. Sanırım basit bir şey eksik. Birisi yardım edebilir mi?

Not: Foobar'ı görmezden gelin, aslında kullanımda paylaşmak istemediğim gerçek bir kimliğim var.


5
Bunu temel sayfa HTML'nize eklemek yerine React aracılığıyla yüklemek için özel bir motivasyon var mı? Bu işe yarasa bile, bileşen her takıldığında bir komut dosyasını yeniden ekleyeceğiniz anlamına gelir.
loganfsmyth

Durum bu mu? DOM farklılığının durumun böyle olmayacağını düşündüm, ancak uygulanmasına bağlı olacağını itiraf ediyorum DocumentTitle.
loganfsmyth

8
@Loganfsmyth'i düzeltin, React bir sonraki durumun betiğe sahip olması durumunda yeniden oluşturma sırasında betiği yeniden yüklemez.
Maksimum

Yanıtlar:


405

Düzenleme: İşler hızlı değişiyor ve bu modası geçmiş - güncellemeye bakın


Komut dosyasını tekrar tekrar, bu bileşen her oluşturulduğunda veya bu bileşen DOM'ye takıldığında yalnızca bir kez almak ve yürütmek istiyor musunuz?

Belki de böyle bir şey deneyin:

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

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

    document.body.appendChild(script);
}

Ancak, bu yalnızca yüklemek istediğiniz komut dosyası modül / paket olarak mevcut değilse gerçekten yararlıdır. İlk olarak, her zaman:

  • Paketi npm'de arayın
  • Paketi projeme indirip kur ( npm install typekit)
  • importİhtiyacım olan paket ( import Typekit from 'typekit';)

Muhtemelen paketleri nasıl kuracağınız reactve react-document-titleörneğin budur ve npm'de bir Typekit paketi bulunmaktadır .


Güncelleme:

Artık kancalarımız olduğuna göre, daha iyi bir yaklaşım şu şekilde kullanmak olabilir 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);
  }
}, []);

Bu da özel bir kanca için harika bir aday olmasını sağlar (örneğin:) 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;

Hangi gibi kullanılabilir:

import useScript from 'hooks/useScript';

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

  // rest of your component
}

Teşekkür ederim. Bunu yanlış düşünüyordum. Bu uygulamaya devam ettim. Sadece try{Typekit.load({ async: true });}catch(e){}sonradan ekleyerekappendChild
ArrayKnight

2
TypeKit'in "gelişmiş" uygulamasının bu yaklaşıma daha uygun olduğuna karar verdim.
ArrayKnight

10
Bu işe yarıyor - betiği yüklemek için, ancak betiğin koduna nasıl erişebilirim? Örneğin, betiğin içinde yaşayan bir işlevi çağırmak istiyorum, ancak betiğin yüklü olduğu bileşenin içinde onu çağıramıyorum.
zero_cool

2
Komut dosyası sayfaya eklendiğinde normal şekilde yürütülür. Örneğin, jQuery'yi bir CDN'den indirmek için bu yöntemi kullandıysanız, componentDidMountişlev komut dosyasını indirip sayfaya ekledikten sonra, jQueryve $nesnelerini genel olarak kullanabilirsiniz (ör: açık window).
Alex McMillan

1
Bir kimlik doğrulama komut dosyası kullanarak benzer bir sorun vardı ve tepki App.js üzerinde bir katman kök html dosyasına dahil etmek daha iyi olabilir çıktı. Herhangi birinin bunu yararlı bulması durumunda. @Loganfsmith'in de belirttiği gibi ...
devssh

57

Yukarıdaki cevaplara ek olarak bunu yapabilirsiniz:

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 bağlı this ve script içine enjekte edilir.

Demoyu codesandbox.io adresinde bulabilirsiniz.


5
this.instance benim için çalışmadı, ama document.body.appendChild Alex McMillan'ın cevabından yaptı.
Ne

6
Muhtemelen this.instanceoluşturma yönteminizdeki ref'ye bağlanmadınız .
Çalıştığını

1
@ShubhamKushwah Sunucu tarafı oluşturuyor musunuz?
ArrayKnight

@ArrayKnight evet daha sonra sunucuda bu nesnelerin olmadığını öğrendim: document, window. Bu yüzden npm globalpaketini kullanmayı tercih ediyorum
Shubham Kushwah

s.async = true nedir, bunun hakkında herhangi bir referans bulamıyorum, amacını bilmek, açıklayabilir misiniz
sasha romanov

50

En sevdiğim yol React Helmet'i kullanmaktır - bu, belge kafasının muhtemelen alıştığınız bir şekilde kolayca değiştirilmesini sağlayan bir bileşendir.

Örneğin

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
Bu açık ara en iyi çözümdür.
paqash

4
Ne yazık ki, çalışmıyor ... Bkz. Codesandbox.io/s/l9qmrwxqzq
Darkowic

2
@Darkowic, ben ekleyerek çalışmalarına kodunuzu var async="true"etmek <script>koduna jQuery, etiketli.
Soma Mbadiwe

@SomaMbadiwe neden çalışıyor async=trueve onsuz başarısız oluyor?
Webwoman

3
Bunu denedim, benim için çalışmıyor. Çıkartılamayan komut dosyasına ekstra özellikler enjekte etmesinin tek nedeni için tepki kaskını kullanmanızı önermem. Bu aslında bazı senaryoları kırıyor ve koruyucular
Philberg

16

<script>SSR'de (sunucu tarafı oluşturma) engellemeniz gerekiyorsa , ile bir yaklaşım componentDidMountçalışmaz.

Bunun react-safeyerine kitaplığı kullanabilirsiniz . React içindeki kod:

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'ın verdiği cevap bana en çok yardımcı oldu, ancak daha karmaşık bir komut dosyası etiketi için pek işe yaramadı.

Ben zaten ek olarak "src" ayarlanmış çeşitli fonksiyonları ile uzun bir etiket için bir çözüm bulmak için biraz cevap tweaked.

(Benim kullanım durumum için betiğin burada da yansıyan kafasında yaşaması gerekiyordu):

  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
Yalnızca sayfa içi satır içi JS'yi sayfaya dökerseniz neden React'i kullandığınızı anlamıyorum ...?
Alex McMillan

2
document.head.removeChild(script);kodunuzu eklemeniz gerekir veya kullanıcı bu sayfa yolunu ziyaret ettiği sürece
html'nizde

7

Bu özel durum için bir React bileşeni oluşturdum: https://github.com/coreyleelarson/react-typekit

Sadece Typekit Kit ID'nizi bir pervane olarak geçmeniz gerekiyor ve gitmeye hazırsınız.

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

Kullanarak çok güzel bir geçici çözüm var 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));
}

Bu, rastgele HTML için çalışır ve ayrıca bağlam bilgilerini de korur document.currentScript.


Kullanım örneği ile nasıl çalışması beklenir? Benim için örneğin geçen senaryo ve vücut ile çalışmıyor ..
Alex Efimov

3

npm postscribeReaksiyon bileşenine komut dosyası yüklemek için kullanabilirsiniz

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


1

Tepki kaskını da kullanabilirsiniz

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

Kask düz HTML etiketleri alır ve düz HTML etiketleri çıkarır. Çok basit ve React acemi dostudur.


0

birden fazla komut dosyası için bunu kullanın

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

En iyi yanıtı aşağıdaki bağlantıda bulabilirsiniz:

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

Göre Alex McMillan 'ın çözümü, ben şu adaptasyon var.
Kendi ortamım: React 16.8+, sonraki v9 +

// Script adlı özel bir bileşen ekleyin
// 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.
}

// Özel bileşimi kullanın, sadece içe aktarın ve eski küçük harf <script>etiketini özel deve kasa <Script>etiketiyle değiştirin.
// 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>

Bu bileşen için bir uyarı var: Bu Komut Dosyası bileşeni yalnızca kendi kardeşlerinin sırasını garanti edebilir. Bu bileşeni aynı sayfanın birden çok bileşeninde birden çok kez kullanırsanız, komut dosyası blokları bozuk olabilir. Bunun nedeni, tüm komut dosyalarının bildirici yerine document.body.appendChild tarafından programlı olarak eklenmesi. Kask, tüm komut dosyası etiketlerini kafa etiketinde taşıyın, ki bu istemiyoruz.
sully

Hey, buradaki sorunum, betiğin DOM'a birkaç kez eklenmesini sağlamak, şimdiye kadar gördüğüm en iyi çözüm, Bileşen Çıkarma sırasında, alt öğeyi (yani <script>) DOM'dan kaldırmak ve sonra bileşen DOM üzerine monte edildiğinde tekrar eklendi (reat-router-dom kullanıyorum ve tüm bileşenlerimin sadece bir bileşeni bu betiğe ihtiyaç duyuyor)
Eazy

0

Partiye biraz geç kaldım ama @Alex Macmillan cevaplarına baktıktan sonra kendiminkini oluşturmaya karar verdim ve bu iki ekstra parametre geçirerek oldu; veya gibi komut dosyalarının yerleştirileceği ve async'in true / false olarak ayarlandığı konum, işte burada:

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;

Onu çağırmanın yolu, bu gönderinin kabul edilen cevabında gösterilenle tamamen aynıdır, ancak iki ekstra (tekrar) parametresiyle:

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

0

Reaksiyon projesinde JS dosyalarını içe aktarmak için bu komutayı kullanın

  1. JS dosyasını projeye taşıyın.
  2. js'yi aşağıdaki komutu kullanarak sayfaya aktarın

Çok basit ve kolaydır.

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

Benim durumumda, bu JS dosyalarını tepki projeme aktarmak istiyorum.


-3

Çözüm senaryoya bağlıdır. Benim durumumda olduğu gibi, bir reaksiyon bileşeninin içine sakin bir şekilde gömmek zorunda kaldım.

Calendly bir div arar ve data-urlözniteliğinden okur ve adı geçen div içine bir iframe yükler.

Sayfayı ilk yüklediğinizde her şey iyidir: ilk olarak div ile data-urlişlenir. Daha sonra vücuda calendly komut dosyası eklenir. Tarayıcı indirir ve değerlendirir ve hepimiz mutlu oluruz.

Sorun, uzaklaşıp sonra sayfaya geri geldiğinizde ortaya çıkar. Bu sefer komut dosyası hala gövdede ve tarayıcı yeniden indirmiyor ve yeniden değerlendirmiyor.

Düzeltme:

  1. Açık componentWillUnmountbulup komut elemanını çıkarın. Ardından yeniden takarken yukarıdaki adımları tekrarlayın.
  2. Enter $.getScript. Bir komut dosyası URI'sı ve başarılı bir geri arama alan bir şık jquery yardımcısıdır. Komut dosyası yüklendikten sonra onu değerlendirir ve başarılı geri aramanızı başlatır. Tek yapmam gereken benim içimde componentDidMount $.getScript(url). Benim renderyöntem zaten calendly div var. Ve düzgün çalışıyor.

2
Bunu yapmak için jQuery eklemek kötü bir fikirdir, ayrıca davanız size çok özeldir. Gerçekte, API'nın yeniden algılama çağrısına sahip olduğundan emin olduğumdan Calendly komut dosyasını bir kez eklerken yanlış bir şey yok. Bir betiği tekrar tekrar kaldırmak ve eklemek doğru değildir.
sidonaldson

@sidonaldson jQuery, bir projeyi devam ettirmeniz gerekiyorsa, farklı tepkilerin (ve kütüphanelerin) mimari bileşimini sadece tepki vermekle kalmaz, aksi takdirde bileşenlere ulaşmak için yerel js kullanmamız gerekir
AlexNikonov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.