Reagieren Sie auf Zeilenumbrüche im gespeicherten Textbereich


81

Facebook nutzen React. Auf einer Einstellungsseite habe ich eine mehrzeilige Seite, textareain die ein Benutzer mehrzeiligen Text eingeben kann (in meinem Fall eine Adresse).

<textarea value={address} />

Wenn ich versuche, die Adresse anzuzeigen, werden {address}die Zeilenumbrüche nicht angezeigt und befinden sich alle in einer Zeile.

<p>{address}</p>

Irgendwelche Ideen, wie man das löst?

Antworten:


240

Es gibt keinen Grund, JS zu verwenden. Mit der white-spaceCSS-Eigenschaft können Sie dem Browser leicht mitteilen, wie mit Zeilenumbrüchen umgegangen werden soll :

white-space: pre-line;

Vorzeile

Leerzeichenfolgen werden reduziert. Zeilen werden bei Zeilenumbrüchen, bei <br>und nach Bedarf unterbrochen, um Zeilenfelder zu füllen.

Schauen Sie sich diese Demo an:

<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-Unterstützung für Pre-Line


Dies funktioniert auf mehrere Arten: Wenn Sie versuchen, einen Zeilenumbruch (<br />) in JSX über eine Variable zu rendern, wird das Markup "sicher" gerendert, anstatt den Zeilenumbruch hinzuzufügen. Die Verwendung von Escape-Zeichen mit diesem CSS umgeht diesen Sicherheitsmechanismus und behebt das angegebene Problem.
Bvoyelr

sehr schöne und einfache Lösung. Jetzt funktionieren alle neuen Zeilenzeichen wie erwartet für texarea.
Namish

31

Dies ist zu erwarten, Sie müssten die neuen Zeilenzeichen (\ n) in HTML-Zeilenumbrüche konvertieren

Ein Artikel über die Verwendung in React : Reagiere Newline, um zu brechen (nl2br)

Artikel zitieren:

Da Sie wissen, dass alles in React Funktionen sind, können Sie dies nicht wirklich tun

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

Da dies eine Zeichenfolge mit DOM-Knoten zurückgeben würde, ist dies ebenfalls nicht zulässig, da es sich nur um eine Zeichenfolge handeln muss.

Sie können dann versuchen, so etwas zu tun:

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

Das ist auch nicht erlaubt, da React wieder reine Funktionen sind und zwei Funktionen nebeneinander liegen können.

tldr. Lösung

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

Jetzt wickeln wir jeden Zeilenumbruch in einen Bereich ein, und das funktioniert einwandfrei, da der Bereich inline angezeigt wird. Jetzt haben wir eine funktionierende nl2br-Zeilenumbruchlösung


Danke Mark, wirkt wie ein Zauber. Gibt es eine Chance, dass Sie die Antwort hier setzen könnten? Stackoverflow mag keine externen Links (könnte verschwinden, die Liste der Antworten ist schwerer zu sehen, ...) und ich werde Ihre Antwort als richtig markieren.
denislexic

4
Hinweis für andere: Sie müssen der Spanne auch ein Schlüsselattribut hinzufügen, da sie sich in einer Schleife befindet. so wäre es....map(function (item, i) { return <span key={i}>{item}<br/></span> })
denislexic

1
Gute Antwort! Das war hilfreich für mich. AUCH wenn Sie MEHRERE Zeilenumbrüche haben, können Sie verhindern, dass der Überschuss <br />für das letzte Element gerendert wird : ....map(function (item, i, arr) { return <span key={i}>{item}{ arr.length-1 === i ? null : <br/>}</span> }). Beachten Sie, dass ich das 3. Argument für.map()
Lasha

1
Ich verstehe nicht, wie du das zum Laufen gebracht hast. Was ich im <Textbereich> angezeigt bekomme, ist[object Object]
Ed.

@ Ed. Es dient dazu, HTML-Text nicht in einem Textbereich anzuzeigen. Hilft das?
Denislexic

8

Die Lösung besteht darin, die Eigenschaft white-spacefür das Element festzulegen, das den Inhalt Ihres textarea:

white-space: pre-line;

4

Petes früherer Vorschlag mit einer eigenständigen Komponente ist eine großartige Lösung, obwohl eine wichtige Sache übersehen wird. Listen benötigen Schlüssel . Ich habe es ein wenig angepasst und meine Version (ohne Konsolenwarnungen) sieht folgendermaßen aus:

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

Es werden die Fragmente von React 16 verwendet


Index als Schlüssel sind nur dann sicher, wenn Sie sicher sind, dass die zugeordneten Daten "statisch" sind und sich in ihrem Lebenszyklus nie ändern. Andernfalls treten wahrscheinlich Probleme damit auf.
Enapupe

2

Ab Reaktion 16 kann eine Komponente ein Array von Elementen zurückgeben. Dies bedeutet, dass Sie eine Komponente wie folgt erstellen können:

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

was du so verwenden würdest:

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

Ich musste '\ n' in '\\ n' ändern.
Dustydojo

1

Liebe Webit-Version. Ich wusste nichts über die Fragment-Komponente, sie ist so nützlich. Die Reduktionsmethode muss jedoch nicht verwendet werden. Karte ist genug. Außerdem benötigt die Liste Schlüssel zum Reagieren, aber es ist eine schlechte Angewohnheit, den Index der iterierenden Methode dafür zu verwenden. Eslint hat dies in meiner Warnung immer wieder zerschlagen, bis ich den Verwirrungsfehler hatte. So würde es aussehen:

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

1
Es ist eine schlechte Idee, UUids als Schlüssel für React zu verwenden, da JEDES neue Rendering dieses Element als neu betrachtet und somit das DOM anpasst.
Enapupe
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.