我有一个相对简单的问题,试图将内联脚本添加到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,我有一个真正的id实际上在使用,我不想分享。
要在head tag <head>中添加脚本标签或代码,请使用react-helmet包。它很轻,有很好的文档。
在script标签中添加Js代码
function htmlDecode(html) {
return html.replace(/&([a-z]+);/ig, (match, entity) => {
const entities = { amp: '&', apos: '\'', gt: '>', lt: '<', nbsp: '\xa0', quot: '"' };
entity = entity.toLowerCase();
if (entities.hasOwnProperty(entity)) {
return entities[entity];
}
return match;
});
}
render() {
const scriptCode = `<script type="text/javascript">
{(function() {
window.hello={
FIRST_NAME: 'firstName',
LAST_NAME: 'lastName',
};
})()}
</script>`
return(
<div dangerouslySetInnerHTML={{ __html: this.htmlDecode(scriptCode) }} />;
);
}
这段代码可以通过console.log(windows.hello)进行测试。
使用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。
与其他答案非常相似,只是使用默认值来清理未定义的检查
import { useEffect } from 'react'
const useScript = (url, selector = 'body', async = true) => {
useEffect(() => {
const element = document.querySelector(selector)
const script = document.createElement('script')
script.src = url
script.async = async
element.appendChild(script)
return () => {
element.removeChild(script)
}
}, [url])
}
export default useScript
使用
useScript('/path/to/local/script.js') // async on body
useScript('https://path/to/remote/script.js', 'html') // async on html
useScript('/path/to/local/script.js', 'html', false) // not async on html.. e.g. this will block
我试图编辑@Alex McMillan接受的答案,但它不让我,所以这里有一个单独的答案,你可以得到你加载的库的值。这是人们要求的非常重要的区别,也是我用stripe.js实现时需要的区别。
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
}
用法是这样的
const PaymentCard = (props) => {
const { Stripe } = useScript('https://js.stripe.com/v2/', 'Stripe')
}
注意:将库保存在一个对象中,因为通常情况下库是一个函数,React会在存储状态时执行函数来检查更改——这将破坏库(如Stripe),期望用特定的参数调用——所以我们将其存储在一个对象中,以隐藏React并保护库函数不被调用。