Реагировать на разрывы строк отображения из сохраненного текстового поля


86

Используя Facebook React. На странице настроек у меня есть textareaмногострочный текст, где пользователь может ввести многострочный текст (в моем случае адрес).

<textarea value={address} />

Когда я пытаюсь отобразить адрес, например {address}, он не показывает разрывы строк и все находится в одной строке.

<p>{address}</p>

Есть идеи, как это решить?

Ответы:


248

Нет причин использовать JS. Вы можете легко указать браузеру, как обрабатывать новую строку, используя white-spaceсвойство CSS:

white-space: 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>

поддержка браузером pre-line


Это работает несколькими способами: если вы попытаетесь отобразить разрыв строки (<br />) в JSX с помощью переменной, он «безопасно» отобразит разметку вместо добавления разрыва строки. Использование escape-символов с этим CSS позволяет обойти этот механизм безопасности, а также устранить указанную проблему.
bvoyelr

очень красивое и простое решение. Теперь все символы новой строки работают, как ожидалось, для texarea.
Namish 04

31

Этого и следовало ожидать, вам нужно будет преобразовать символы новой строки (\ n) в разрывы строк HTML.

Статья об использовании в 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 снова представляет собой чистые функции, и две функции могут быть рядом друг с другом.

tldr. Решение

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

Теперь мы оборачиваем каждый разрыв строки в span, и это отлично работает, потому что у span есть отображение inline. Теперь у нас есть работающее решение для переноса строки в nl2br


Спасибо, Марк, работает как шарм. Есть ли шанс, что вы могли бы написать здесь ответ? Stackoverflow не любит внешние ссылки (могут исчезнуть, сложнее просмотреть список ответов ...), и я отмечу ваш ответ как правильный.
denislexic

4
Примечание для других: вам также необходимо добавить ключевой атрибут в диапазон, потому что он находится в цикле. так и будет....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> }). Обратите внимание, что я включил 3-й аргумент для.map()
Лаша

1
Я не понимаю, как ты заставил это работать. В <textarea> я вижу[object Object]
Ред.

@Ed. Это для отображения текста HTML не в текстовом поле. Это помогает?
denislexic

11

Решение состоит в том, чтобы установить свойство white-spaceв элементе, отображающем содержимое вашего textarea:

white-space: pre-line;

4

Предыдущее предложение Пита с автономным компонентом - отличное решение, хотя оно упускает одну важную вещь. Спискам нужны ключи . Я немного поправил, и моя версия (без предупреждений консоли) выглядит так:

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

Он использует фрагменты React 16


index как ключи безопасны только в том случае, если вы уверены, что сопоставленные данные «статичны» и никогда не изменятся в течение своего жизненного цикла, иначе у вас могут возникнуть проблемы с ними.
enapupe

1

Начиная с 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

1

Люблю версию webit. Я не знал о компоненте «Фрагмент», он очень полезен. Однако нет необходимости использовать метод уменьшения. Карты достаточно. Кроме того, списку нужны ключи в реакции, но использовать для него индекс из метода итерации - плохая привычка. eslint продолжал разбивать это в моем предупреждении, пока у меня не возникла ошибка путаницы. Так это выглядело бы так:

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

1
Использование uuids в качестве ключа в React - плохая идея, потому что КАЖДЫЙ новый рендер будет считать этот элемент новым и, таким образом, адаптировать DOM.
enapupe
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.