从服务器渲染HTML字符串中的自定义反应组件

时间:2017-08-15 16:38:39

标签: javascript reactjs

我有一个来自服务器的HTML字符串,例如:

const myString = '<p>Here goes the text [[dropdown]] and it continues</p>`;

我将这个字符串分成3个部分,结果如下:

const splitString = [
  '<p>Here goes the text ',
  '[[dropdown]]',
  ' and it continues</p>'
];

然后我处理这3个部分,以便用反应成分替换下拉列表:

const processedArr = splitString.map((item) => {
  if (/* condition that checks if it's `[[dropdown]]` */) {
    return <Dropdown />;
  }
  return item;
}

毕竟,我得到了处理后的数组,如下所示:

['<p>Here goes the text ', <Dropdown />, ' and it continues</p>']

当我渲染它时,它将HTML作为文本(显然)呈现为文本之间的Dropdown组件(正确呈现)。这里的问题是我无法使用{ __html: ... },因为必须使用<div dangerouslySetInnerHTML={{ __html: ... }} />。我无法在字符串周围添加<div>,因为这样会删除<p>标记。

我考虑过将部分拆分成标签,然后在某种循环中执行类似的操作:

React.createElement(tagName, null, firstTextPart, reactComponent, secondTextPart);

但这需要相当复杂的逻辑,因为在一个[[dropdown]]标记内可能有多个<p>,并且也可能存在嵌套标记。

所以我现在被困住了。也许我从一个非常奇怪的角度看问题,这可以在React中以不同的方式实现。我知道React社区不鼓励从字符串中呈现HTML,但我不能解决这个问题,我总是要从服务器接收文本。

我发现唯一相关的stackoverflow问题是this one,但是假设来自后端的内容总是具有相同的结构,因此在我的情况下不能使用内容可以是任何内容。

编辑: 经过一番挖掘,我发现这question and answer似乎有点解决了我的问题。但是使用react-dom/server包及其renderToString方法将我的组件转换为字符串然后连接它仍然感觉有点奇怪。但我会尝试一下,如果有效并符合我的需要,会发布更多信息。

2 个答案:

答案 0 :(得分:0)

所以在玩完代码之后,我终于找到了一个“解决方案”。它并不完美,但我还没有找到任何其他方法来完成我的任务。

我没有按照我的方式处理splitString.map看起来会有所不同:

// Reset before `.map` and also set it up in your component's constructor.
this.dropdownComponents = [];

const processedArr = splitString.map((item) => {
  if (/* condition that checks if it's `[[dropdown]]` */) {
    const DROPDOWN_SELECTOR = `dropdown-${/* unique id here */}`;
    this.dropdownComponents.push({
      component: <Dropdown />,
      selector: DROPDOWN_SELECTOR
    });
    return `<span id="${DROPDOWN_SELECTOR}"></span>`;
  }
  return item;
}).join('');

然后,对于componentDidMountcomponentDidUpdate,请调用以下方法:

  _renderDropdowns() {
    this.dropdownComponents.forEach((dropdownComponent) => {
      const container = document.getElementById(dropdownComponent.selector);
      ReactDOM.render(dropdownComponent.component, container);
    });
  }

它将确保具有特定下拉列表ID的span标记内的内容将被组件替换。在componentDidMountcomponentDidUpdate中使用上述方法可确保在传递任何新道具时,道具将会更新。在我的例子中,我没有传递任何道具,但在现实世界的例子中,你通常会传递道具。

毕竟,我不必使用react-dom/server renderToString

答案 1 :(得分:0)

如何将文本分开并单独渲染组件?

你的反应组件应如下所示(在JSX中):

<div>
    <span>first text</span>
    {props.children} // the react component you pass to render
    <span>second part of the text</span>
</div>

你只需用以下内容调出这个组件:

<MessageWrapper>
    <DropdownComponent/> // or whatever
</MessageWrapper>