javascript - React/JSX'e script etiketi eklemek


React bileşenine satır içi betik eklemeye çalışmak gibi nispeten basit bir sorunum var. Şu ana kadar sahip olduklarım:

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

Ben de denedim:

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

Hiçbir yaklaşım istenen betiği çalıştırıyor gibi görünmüyor. Tahminimce kaçırdığım basit bir şey var. Birisi yardımcı olabilir mi?

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

Cevap #1

Düzenleme: İşler hızlı değişiyor ve bu güncel değil - güncellemeye bakın


Bu bileşen her render edildiğinde betiği tekrar tekrar alıp çalıştırmak mı istiyorsunuz, yoksa bu bileşen DOM'a monte edildiğinde sadece bir kez mi?

Belki bunun gibi bir şey deneyebilirsin:

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 betik bir modül/paket olarak mevcut değilse gerçekten yararlıdır. Öncelikle, her zaman şunu yapardım:

  • Paket için npm üzerinde arama yap
  • Paketimi projemde indirin ve kurun (npm install typekit)
  • import paketini ihtiyacım olan yerde kullan (import Typekit from 'typekit';)

Bu muhtemelen örneğinizden react ve react-document-title paketlerini nasıl yüklediğinizdir ve npm'de mevcut olan bir Typekit paketi bulunmaktadır.


Güncelleme :

Artık hook'larımıza sahip olduğumuza göre, daha iyi bir yaklaşım useEffect'i şöyle kullanmak olabilir:

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 onu özel bir hook (örneğin: hooks/useScript.js) için harika bir aday yapar:

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;

Şöyle kullanılabilir:

import useScript from 'hooks/useScript';

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

  // rest of your component
}

Cevap #2

En sevdiğim yöntem React Helmet kullanmak - bu, muhtemelen zaten alışık olduğunuz şekilde belge başlığını kolayca manipüle etmeye izin veren bir bileşendir.

ör.

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

Cevap #3

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 this'e bağlanmıştır ve betik içine enjekte edilmiştir.

Demo codesandbox.io adresinde bulunabilir

Cevap #4

Bu cevap, bu davranışın arkasındaki nedeni açıklar.

Herhangi bir yaklaşım, render script etiketini beklenildiği gibi çalıştırmaz:

  1. Harici betikler için script etiketi kullanma
  2. dangerouslySetInnerHTML kullanımı

Neden

React DOM (web üzerinde React için renderer) JSX'i DOM elementlerine render etmek için createElement çağrılarını kullanır.

createElement, bunları nihayetinde DOM'a eklemek için innerHTML DOM API'sini kullanır (React kaynağındaki koda bakın). innerHTML, bir güvenlik önlemi olarak eklenen script etiketini çalıştırmaz. İşte bu nedenle, React'te script etiketlerini render etmek beklenildiği gibi çalışmaz.

React'te script etiketlerini nasıl kullanacağınızı öğrenmek için bu sayfadaki diğer cevaplara bakın.

Cevap #5

Eğer SSR (sunucu tarafı render) içinde <script> bloğuna ihtiyacınız varsa, componentDidMount ile yapılan bir yaklaşım işe yaramayacaktır.

Bunun yerine react-safe kütüphanesini kullanabilirsiniz. React'teki kod şu şekilde olacak:

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>

Cevap #6

Alex Mcmillan'ın verdiği cevap bana en çok yardımcı oldu ama daha karmaşık bir script etiketi için tam olarak işe yaramadı.

Farklı işlevlere sahip uzun bir etiket için ve ayrıca zaten "src" ayarlıyor olan bir çözüm bulmak için cevabını biraz değiştirdim.

(Benim kullanım durumum için betiğin head içinde yer alması gerekiyordu, bu da burada yansıtılmıştır):

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

Cevap #7

@Alex McMillan tarafından kabul edilen cevabı düzenlemeye çalıştım ama izin vermedi, bu yüzden kütüphaneyi yüklerken değerini alabileceğiniz ayrı bir cevap burada. İnsanların sorduğu ve benim stripe.js ile uygulamam için ihtiyaç duyduğum çok önemli bir ayrım.

useScript.js

import { useState, useEffect } from 'react'

export const useScript = (url, name) => {

  const [lib, setLib] = useState({})

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

    script.src = url
    script.async = true
    script.onload = () => setLib({ [name]: window[name] })

    document.body.appendChild(script)

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

  return lib

}

kullanım şöyle görünüyor

const PaymentCard = (props) => {
  const { Stripe } = useScript('https://js.stripe.com/v2/', 'Stripe')
}

NOT: Kütüphaneyi bir nesne içinde kaydediyoruz çünkü çoğu zaman kütüphane bir fonksiyondur ve React durumu kaydederken değişiklikleri kontrol etmek için fonksiyonu çalıştırır -- bu, belirli argümanlarla çağrılmayı bekleyen kütüphaneleri (Stripe gibi) bozabilir -- bu yüzden bunu bir nesne içinde saklıyoruz, böylece React'ten gizlemiş ve kütüphane fonksiyonlarının çağrılmasını korumuş oluyoruz.

Cevap #8

Aynı zamanda react helmet 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>
    );
  }
};

Helmet basit HTML etiketlerini alır ve basit HTML etiketleri olarak çıktılar. Oldukça basittir ve React yeni başlayanlar için uygundur.

İlgili makaleler