Reaguj na podziały wierszy wyświetlania z zapisanego obszaru tekstowego


86

Korzystanie z Facebooka React. Na stronie ustawień mam multilinię, w textareaktórej użytkownik może wprowadzić tekst wielowierszowy (w moim przypadku adres).

<textarea value={address} />

Kiedy próbuję wyświetlić adres, więc coś w stylu {address}, nie pokazuje podziałów linii i jest w jednej linii.

<p>{address}</p>

Jakieś pomysły, jak to rozwiązać?

Odpowiedzi:


248

Nie ma powodu, aby używać JS. Możesz łatwo powiedzieć przeglądarce, jak obsługuje znak nowej linii, używając white-spacewłaściwości CSS:

white-space: pre-line;

pre-line

Sekwencje białych znaków są zwinięte. Wiersze są przerywane w znakach nowej linii, w <br>i w razie potrzeby, aby wypełnić pola linii.

Sprawdź to demo:

<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>

obsługa przeglądarki dla pre-line


Działa to na wiele sposobów: jeśli spróbujesz wyrenderować podział wiersza (<br />) w JSX za pomocą zmiennej, „bezpiecznie” wyrenderuje znacznik zamiast dodawać podział wiersza. Używanie znaków ucieczki w tym CSS pozwala obejść ten mechanizm bezpieczeństwa, a także naprawić wspomniany problem.
bvoyelr

bardzo ładne i proste rozwiązanie. Teraz wszystkie nowe znaki linii działają zgodnie z oczekiwaniami dla texarei.
Namish

31

Należy się tego spodziewać, trzeba by przekonwertować znaki nowego wiersza (\ n) na znaki końca wiersza HTML

Artykuł o używaniu go w React : React Newline to break (nl2br)

Aby zacytować artykuł:

Ponieważ wiesz, że wszystko w React jest funkcjami, nie możesz tego zrobić

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

Ponieważ zwróciłoby to łańcuch z węzłami DOM w środku, to też nie jest dozwolone, ponieważ musi to być tylko ciąg.

Następnie możesz spróbować zrobić coś takiego:

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

To też jest niedozwolone, ponieważ ponownie React jest czystymi funkcjami i dwie funkcje mogą znajdować się obok siebie.

tldr. Rozwiązanie

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

Teraz zawijamy każdy znak końca wiersza w rozpiętości i to działa dobrze, ponieważ rozpiętość ma wbudowany wyświetlacz. Teraz mamy działające rozwiązanie podziału wiersza nl2br


Dzięki Mark, działa jak urok. Jakaś szansa, że ​​mógłbyś tu umieścić odpowiedź? Stackoverflow nie lubi linków zewnętrznych (mogą zniknąć, trudniej będzie wyświetlić listę odpowiedzi, ...) i oznaczę Twoją odpowiedź jako poprawną.
denizlexic

4
Uwaga dla innych: musisz również dodać atrybut klucza do zakresu, ponieważ jest w pętli. tak by było....map(function (item, i) { return <span key={i}>{item}<br/></span> })
denislexic

1
Świetna odpowiedź! To było dla mnie pomocne. Ponadto, jeśli masz wiele podziałów wierszy, można zapobiec nadwyżka <br />ze świadczenia na ostatniej pozycji: ....map(function (item, i, arr) { return <span key={i}>{item}{ arr.length-1 === i ? null : <br/>}</span> }). Zauważ, że dołączyłem trzeci argument za.map()
Lasha

1
Nie rozumiem, jak to działa. W <textarea> pojawia się[object Object]
wyd.

@Ed. Służy do wyświetlania tekstu HTML, a nie w obszarze tekstowym. To pomaga?
denizlexic

11

Rozwiązaniem jest ustawienie właściwości white-spacena elemencie wyświetlającym zawartość twojego textarea:

white-space: pre-line;

4

Poprzednia propozycja Pete'a z samodzielnym komponentem jest świetnym rozwiązaniem, chociaż brakuje w niej jednej ważnej rzeczy. Listy wymagają kluczy . Poprawiłem to trochę i moja wersja (bez ostrzeżeń konsoli) wygląda tak:

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

Używa fragmentów React 16


indeks jako klucze są bezpieczne tylko wtedy, gdy masz pewność, że mapowane dane są „statyczne” i nigdy nie zmienią się w swoim cyklu życia, w przeciwnym razie prawdopodobnie wystąpią z nimi problemy.
enapupe

1

Od React 16 komponent może zwracać tablicę elementów, co oznacza, że ​​możesz stworzyć komponent taki jak ten:

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

którego możesz użyć w ten sposób:

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

Musiałem zmienić „\ n” na „\\ n”.
dustydojo

1

Uwielbiam wersję webit. Nie wiedziałem o komponencie Fragment, jest tak przydatny. Nie ma jednak potrzeby stosowania metody redukcji. Mapa wystarczy. Ponadto lista wymaga kluczy w reakcji, ale używanie indeksu z metody iteracyjnej jest złym nawykiem. eslint niszczył to w moim ostrzeżeniu, aż pojawił się błąd zamieszania. Więc wyglądałoby to tak:

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

1
Używanie uuidów jako klucza w Reakcie to zły pomysł, ponieważ KAŻDY nowy render uzna ten element za nowy i dostosuje DOM.
enapupe
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.