React展示保存的文本区域中的换行符

142

使用Facebook的React库。 在一个设置页面中,我有一个多行文本框textarea,用户可以输入多行文本(例如地址)。

<textarea value={address} />

当我试图显示地址,就像{address}这样,它不会展示换行符,而是全在一行上。

<p>{address}</p>

你有什么解决这个问题的想法吗?


我花了很多时间来调试这个问题,所以我把这个留给未来像我一样的自己:如果你认为将内容保存到React状态中会移除换行符,请检查一下你的React状态。 - Chinmay Ghule
7个回答

390

没有使用JS的必要。您可以轻松地告诉浏览器如何处理换行符,只需使用white-space CSS属性即可:

white-space: pre-line;

pre-line

空格序列被折叠。在换行符处、<br>处以及必要时进行分行。

查看此演示:

<style>
  #p_wrap {
    white-space: pre-line;
  }
</style>

<textarea id="textarea"></textarea>
<p id="p_standard"></p>
<hr>
<p id="p_wrap"></p>
<script>
  textarea.addEventListener('keypress', function(e) {
    p_standard.textContent = e.target.value
    p_wrap.textContent = e.target.value
  })
</script>

browser support


这个有多种解决方法:如果你尝试通过变量在JSX中渲染一个换行符(<br />),它会“安全地”渲染标记而不是添加换行符。使用转义字符与CSS一起使用可以绕过该安全机制并修复所述问题。 - bvoyelr
非常好且简单的解决方案。现在所有的换行符都按照texarea的预期工作。 - Namish
这是我感觉需要多次点赞的答案之一。非常感谢 @enanupe。 - Joshua Kusaasira
这就是它。 - Steven Baughman
我不知道这里发生了什么,但在使用React 18的Chrome浏览器中,这对我来说不起作用,它只是渲染了\n。 - undefined

46

可以预料到,你需要将换行符(\n)转换为HTML换行符<br />

一篇关于在React中使用它的文章: React Newline to break (nl2br)

引用文章内容:

因为你知道React中的所有东西都是函数,所以你真的做不到这点。

this.state.text.replace(/(?:\r\n|\r|\n)/g, '<br />')

由于返回一个包含DOM节点的字符串,这也是不被允许的,因为必须只是一个字符串。

然后你可以尝试像这样做:

{this.props.section.text.split(“\n”).map(function(item) {
  return (
    {item}
    <br/>
  )
})}    

那也是不允许的,因为 React 是纯函数,而且两个函数不能挨在一起。

简而言之,解决方案是:

{this.props.section.text.split(“\n”).map(function(item) {
  return (
    <span>
      {item}
      <br/>
    </span>
  )
})}

现在我们正在用一个 span 包装每个换行符,这很好的工作是因为 span 具有内联显示属性。现在我们得到了一个有效的 nl2br 换行解决方案。


谢谢Mark,非常好用。你可以把答案放在这里吗?Stackoverflow不喜欢外部链接(可能会消失,更难查看答案列表...),我会标记你的答案为正确答案。 - denislexic
4
注意:其他人还需要向span添加一个key属性,因为它在循环中。所以应该是....map(function (item, i) { return <span key={i}>{item}<br/></span> }) - denislexic
1
太棒了!这对我非常有帮助。另外,如果您有多个换行符,可以避免在最后一项中呈现多余的<br/>...map(function (item, i, arr) {return <span key={i}>{item}{arr.length-1 === i ? null : <br/>}</span>})。请注意,我包含了.map()的第三个参数。 - Lasha
1
我不明白你是如何让它工作的。在<textarea>中显示的是[object Object] - Ed.
如果你想保留单个换行符,但将两个或两个以上的换行符视为段落分隔符,请使用以下代码:return text.trim().split(/\n{2,}/).map(p => <p>{p}</p>) - Kip
显示剩余2条评论

20

解决方案是在显示内容的元素上设置white-space属性:

white-space: pre-line;

4

Pete之前提出的独立组件方案非常好,尽管它缺少了一个重要的东西。列表需要keys。我稍微调整了一下,我的版本(没有控制台警告)看起来像这样:

const NewLineToBr = ({ children = '' }) => children.split('\n')
  .reduce((arr, line, index) => arr.concat(
    <Fragment key={index}>
      {line}
      <br />
    </Fragment>,
  ), [])

它使用React 16的片段

如果您确定映射的数据是“静态”的,并且在其生命周期内永远不会更改,那么使用索引作为键是安全的。否则,您可能会遇到问题。 - enapupe

3
一个小补充: 应该将white-space属性与word-wrap一起使用,以防止溢出。
p {
  white-space: pre-wrap;
  word-wrap: break-word;   
}

我想你是指 word-break 吧?word-wrap 不在 CSS 规范中。 - Liam
https://www.w3schools.com/cssref/css3_pr_word-wrap.php - Abdulhakim
这正是我们不喜欢w3schools的原因。那些信息已经过时。word-wrap是一种非标准且已过时的实现,不应再使用。出于遗留原因,用户代理必须将word-wrap视为overflow-wrap属性的遗留名称别名 - Liam
我并没有攻击任何人。这个答案现在是不正确的。我并不关心问题来自何时。任何现在看到这个答案的人都应该知道,这种语法已经过时,不应该再使用。顺便说一下,这在两年前和现在都是正确的。 - Liam

2

从React 16开始,组件可以返回一个元素数组,这意味着你可以创建如下的组件:

export default function NewLineToBr({children = ""}){
  return children.split('\n').reduce(function (arr,line) {
    return arr.concat(
      line,
      <br />
    );
  },[]);
}

您可以像这样使用它:

<p>
  <NewLineToBr>{address}</NewLineToBr>
</p>

我不得不将'\n'更改为'\n'。 - dustydojo

0

喜欢 webit 版本。我不知道 Fragment 组件,它非常有用。但是不需要使用 reduce 方法。Map 就足够了。此外,在 React 中,列表确实需要键,但从迭代方法使用索引是一个不好的习惯。ESLint 一直在我的警告中强调这一点,直到我遇到混淆错误。所以代码应该像这样:

const NewLine = ({ children }) =>
   children.split("\n").map(line => (
    <Fragment key={uuidv4()}>
      {line}
      <br />
    </Fragment>
  ));

4
在React中使用uuid作为键是个不好的主意,因为每次渲染都会将此元素视为新元素,从而调整DOM。 - enapupe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接