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

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

Enapupe
источник
Это работает несколькими способами: если вы попытаетесь отобразить разрыв строки (<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;
OualidAj
источник
4

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

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

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

webit
источник
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