简体   繁体   English

我怎样才能渲染一个<template></template>标记反应?

[英]How can I render a <template /> tag with react?

Sometimes you might need to render web-components from your react app.有时您可能需要从您的 React 应用程序呈现 Web 组件。

Web-components often use a special <template>... </template> tag. Web 组件通常使用特殊的<template>... </template>标签。 But if I try to render such markup with react like this:但是如果我尝试用这样的反应来渲染这样的标记:

render() {
  return (
    <template>
      <div>some content</div>
    </template>
  )
}

then my web-components don't work correctly.那么我的网络组件无法正常工作。

The reason is that JSX does a different job than what the <template /> tags exists for.原因是 JSX 与<template />标签的作用不同。 The idea of a template tag is to not render its children and pretty much handle it like unparsed text (the browser actually parses it just to make sure its valid html, but does nothing more)模板标签的想法是不渲染它的子标签,并且几乎像未解析的文本一样处理它(浏览器实际上解析它只是为了确保其有效的 html,但仅此而已)

But when you write this in JSX:但是当你用 JSX 写这个时:

return (
  <template>
    <div>some content</div>
  </template>
)

you're basically instructing react to create a 'template' element and then create a 'div' element and then to append this div to the template as a child.您基本上是在指示 react 创建一个'template'元素,然后创建一个'div'元素,然后将此div作为子项附加到template中。

So under hood this happens:所以在引擎盖下会发生这种情况:

const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)

But what you want is to set the contents of the <template /> as a string.但是您想要的是将<template />的内容设置为字符串。 You can use innerHTML for that.您可以为此使用innerHTML


Solution解决方案

One solution would be:一种解决方案是:

render() {
  return (
    <template
      dangerouslySetInnerHTML={{
        __html: '<div>some content</div>'
      }}
    />
  )
}

Now you're asking react to create all those children tags as node elements but letting the browser decide what to do with them.现在您要求 react 创建所有这些子标签作为节点元素,但让浏览器决定如何处理它们。

Nicer solution更好的解决方案

You might not want to use dangerouslySetInnerHTML all the time.您可能不想一直使用dangerouslySetInnerHTML的SetInnerHTML。 So let's create a helper component:所以让我们创建一个辅助组件:

function Template({ children, ...attrs }) {
  return (
    <template
      {...attrs}
      dangerouslySetInnerHTML={{ __html: children }}
    />
  );
}

Now any time you need to use a template you can use it like this:现在,任何时候您需要使用模板时,您都可以像这样使用它:

render() {
  return (
    <Template>
      {'<div>some content</div>'}
    </Template>
  )
}

Don't forget to put the inner content in quotes, because it should be a string.不要忘记将内部内容放在引号中,因为它应该是一个字符串。

I know that this question has already an answer, but there is another, I guess simpler solution to do that creating hoc (Higher Order Component).我知道这个问题已经有了答案,但我想还有另一个更简单的解决方案来创建 hoc(高阶组件)。

Just create new "component" like this:只需像这样创建新的“组件”:

// hoc/Template.js
const template = props => props.children
export default template

and then you can use it in your project this way:然后你可以这样在你的项目中使用它:

import './hoc/Template.js'

...

render() {
  return (
    <Template>
      {'<div>some content</div>'}
    </Template>
  )
}

Newer version of react has already build it such a component, so you can achieve same thing without creating component.较新版本的 react 已经构建了这样一个组件,所以你可以在不创建组件的情况下实现同样的事情。

import { Fragment } from 'react'

...

render() {
  return (
    <Fragment>
      {'<div>some content</div>'}
    </Fragment>
  )
}

This is actually a bug in React!这实际上是 React 中的一个错误! https://github.com/facebook/react/issues/19932 https://github.com/facebook/react/issues/19932

When the native browser parses the <template> tag, it appends all children to the template's content fragment, instead of adding them as children .当本机浏览器解析<template>标记时,它会将所有子项附加到模板的content片段,而不是将它们添加为children So a template should never actually have any children .所以一个模板实际上不应该有任何children

However, if you programmatically build the DOM (the way react-dom does), then the template will have an empty content fragment, since all children are added as children.但是,如果您以编程方式构建 DOM( react-dom的方式),那么模板将有一个content片段,因为所有子项都作为子项添加。 This is why web components will not behave properly with these templates.这就是 web 组件在这些模板下无法正常运行的原因。

Here's an example of the problem too: https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118这也是问题的示例: https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118

The easiest workaround is to use dangerouslySetInnerHTML , like so:最简单的解决方法是使用dangerouslySetInnerHTML ,如下所示:

<template dangerouslySetInnerHTML={{ __html: `
  <p> Some text </p>
` }} />

This is limited by the fact that you can only supply raw HTML (no custom React elements).这是受限于您只能提供原始 HTML(没有自定义 React 元素)这一事实。 However, since you're using the <template> tag in the first place, it seems highly unlikely that you'd also be using React within the template.但是,由于您一开始就使用了<template>标签,因此您似乎不太可能在模板中使用 React。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM