渲染库组件存在问题: 我从服务器
获取带有html的字符串let serverResponse = `
<h3>Some title</h3>
<p>Some text</p>
<p>
<img src="">
<img src="">
<img src="">
<br>
</p>
...
`
现在我使用dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: serverResponse }} />
但是当我得到2个或更多重复的<img>
标签时,我想用组件替换它们。
我怎样才能做到这一点?我尝试使用Regex
执行此操作并将其替换为<Gallery/>
,但它不起作用。我认为我需要在标签数组中拆分字符串,然后用<Gallery/>
组件替换图像。
我试过renderToString
...
getGallary = images => {
// Loop throw images and get sources
let sources = [];
if (images) {
images.map(img => {
let separatedImages = img.match(/<img (.*?)>/g);
separatedImages.map(item => sources.push(...item.match(/(https?:\/\/.*\.(?:png|jpg))/)));
});
}
if (sources.length) {
return <Gallery items={sources}>
}
return <div/>
};
...
<div dangerouslySetInnerHTML={{__html: serverResponse.replace(/(<img (.*?)>){2,}/g,
renderToString(this.getGallary(serverResponse.match(/(<img (.*?)>){2,}/g))))}}/>}
这不起作用,因为我只得到没有逻辑的HTML :(
答案 0 :(得分:1)
所有的拳头,dangerouslySetInnerHTML
不是要走的路,你不能在其中插入图库并让它由React处理。你需要做的是多步骤程序。
<强> 1。将HTML解析为文档。在此阶段,您将字符串转换为有效的DOM文档。使用DOMParser非常容易:
function getDOM (html) {
const parser = new DOMParser()
const doc = parser.parseFromString(`<div class="container">${html}</div>`, 'text/html')
return doc.querySelector('.container')
}
我使用这个帮助函数返回带有HTML节点的容器。下一步将需要它。
<强> 2。将DOM文档转换为React JSX树。现在您已经拥有了DOM树,通过从相应的DOM节点创建单独的React元素,可以很容易地将其转换为JSX。此函数需要递归才能处理DOM树的所有级别。这样的事情会做:
function getJSX(root) {
return [...root.children].map(element => {
const children = element.children.length ? getJSX(element) : element.textContent
const props = [...element.attributes].reduce((prev, curr) => ({
...prev,
[curr.name]: curr.value
}), {})
return React.createElement(element.tagName, props, children)
})
}
这足以从DOM创建JSX。它可以像这样使用:
const JSX = getJSX(getDOM(htmlString))
第3。注入图库。现在,如果element
包含多于1个图像标记,则可以改进JSX创建以将Gallery注入已创建的JSX。我会将注入函数传递给getJSX
作为第二个参数。与上述版本的唯一区别在于如何在图库中计算children
:
if (element.querySelector('img + img') && injectGallery) {
const imageSources = [...element.querySelectorAll('img')].map(img => img.src)
children = injectGallery(imageSources)
} else {
children = element.children.length ? getJSX(element) : element.textContent
}
<强> 4。创建图库组件。现在是创建图库组件的时候了。该组件将如下所示:
import React from 'react'
import { func, string } from 'prop-types'
function getDOM (html) {
const parser = new DOMParser()
const doc = parser.parseFromString(`<div class="container">${html}</div>`, 'text/html')
return doc.querySelector('.container')
}
function getJSX(root, injectGallery) {
return [...root.children].map(element => {
let children
if (element.querySelector('img + img') && injectGallery) {
const imageSources = [...element.querySelectorAll('img')].map(img => img.src)
children = injectGallery(imageSources)
} else {
children = element.children.length ? getJSX(element) : element.textContent
}
const props = [...element.attributes].reduce((prev, curr) => ({
...prev,
[curr.name]: curr.value
}), {})
return React.createElement(element.tagName, props, children)
})
}
const HTMLContent = ({ content, injectGallery }) => getJSX(getDOM(content), injectGallery)
HTMLContent.propTypes = {
content: string.isRequired,
injectGallery: func.isRequired,
}
export default HTMLContent
<强> 5。使用它!以下是您将如何一起使用的方法:
<HTMLContent
content={serverResponse}
injectGallery={(images) => (
<Gallery images={images} />
)}
/>
以上是此代码的演示。
答案 1 :(得分:0)
TLDR:您可以使用React HTML Parser或类似的库。
虽然它看起来非常相似,但JSX被解析为一堆React.createElement
,因此将插入的React组件转换为HTML字符串将无法正常工作。 renderToString
也不会这样做,因为它用于服务器端渲染React页面,并且在您的情况下不起作用。
要用React组件替换HTML标记,您需要一个解析器将HTMl字符串解析为节点,将节点映射到React元素并渲染它们。幸运的是,有一些库可以做到这一点,比如React HTML Parser。