Попытка создать компонент React с текстовой областью, которая автоматически расширяется и сжимается в зависимости от высоты содержимого (с изолированной средой)

#javascript #css #reactjs #styled-components #react-hooks

#javascript #css #reactjs #styled-компоненты #реагирующие крючки

Вопрос:

Я пытаюсь создать этот TextArea компонент, который использует React hook и styled-components .

Все идет хорошо, когда мой textarea набирает новые строки. Функция расширения работает так, как ожидалось.

Что я делаю, в основном, это:

  • Использование textAreaRef useRef() для сохранения ссылки на textarea элемент
  • Использование lastScrollHeight useRef() для сохранения последнего ScrollHeight
  • Использование idealHeight useRef() для сохранения идеальной высоты для textarea

Я вычисляю idealHeight на основе значения lastScrollHeight , которое было сохранено при предыдущем рендеринге. Затем я сравниваю с текущим значением textAreaRef.current.scrollHeight и вычисляю delta .

Затем я вычисляю idealHeight.current = idealHeight.current delta и отправляю его как props моему стилизованному компоненту TextAreaInput , чтобы установить height .

ПРОБЛЕМА

scrollHeight увеличивается при добавлении новых строк и возвращает положительное значение delta . Но scrollHeight не уменьшается при удалении строк. Таким образом, delta возвращается ноль.

Мне нужен способ измерить высоту текстового содержимого внутри textarea . Я видел здесь несколько вопросов о SO, которые рекомендовали сделать это, установив для height элемента очень маленькое значение, прежде чем запрашивать scrollHeight .

Действительно, это дает мне отрицательный результат deltas , который я хочу, но это просто останавливает любое расширение или сжатие, и вместо этого я получаю полосу прокрутки. Убедитесь в этом, переключив строку с комментариями textAreaRef.current.style.height = "2px"; . Некоторые способы зависания компонента height='2px' . Я не знаю.

Изолированная среда кода с рабочим примером (обратите внимание на журналы консоли)

Есть идеи, что происходит не так?

TextArea.js

 import React, { useRef } from "react";
import styled from "styled-components";

const TextAreaInput = styled.textarea`
  height: ${props => props.idealHeight || "152px"};
  min-height: 32px;
  max-height: 320px;
  line-height: 21px;
  width: 100%;
  resize: vertical;
  box-sizing: border-box;

  border: 1px solid rgb(217, 217, 217);
  padding: 4px 11px;
  text-size-adjust: 100%;
`;

function TextArea(props) {
  const idealHeight = useRef(32);
  const lastScrollHeight = useRef(30);
  const textAreaRef = useRef(null);

  console.log("ANOTHER RENDER...");

  if (textAreaRef.current !== null amp;amp; textAreaRef.current !== undefined) {
    const scrollHeight = textAreaRef.current.scrollHeight;

    // THIS NEXT LINE MAKES THE DELTA CALCULATION CORRECT ON 'SHRINKING'
    // BUT STOPS THE RESIZING

    // textAreaRef.current.style.height = "2px";

    const delta = scrollHeight - lastScrollHeight.current;
    console.log("Delta is: "   delta);

    console.log("Last ScrollHeight: "   lastScrollHeight.current);
    lastScrollHeight.current = scrollHeight;

    console.log("Current ScrollHeight: "   lastScrollHeight.current);

    idealHeight.current = idealHeight.current   delta;

    console.log("IdealHeight :"   idealHeight.current);
  }

  return (
    <TextAreaInput
      placeholder={props.placeholder}
      value={props.value}
      onChange={e => props.setValue(e.target.value)}
      ref={textAreaRef}
      idealHeight={idealHeight.current   "px"}
    />
  );
}

export default TextArea;
  

Ответ №1:

Просто придумал решение:

Чтобы получить содержимое height внутри textarea , сначала нам нужно установить его height свойству значение 0px , а затем получить его scrollHeight .

Но когда вы делаете это, вы в конечном итоге создаете встроенный стиль с height=0px , и он получает наивысший приоритет правил CSS, поэтому вам нужно отменить его с помощью:

textAreaRef.current.removeAttribute('style');

Это необходимо, потому что весь мой CSS применяется styled-components, который использует <style> html-тег внутри <head> , который имеет более низкий приоритет, чем встроенный CSS.

Итак, окончательный и рабочий код:

TextArea.js

 function TextArea(props) {

  const idealHeight = useRef(32);
  const lastScrollHeight = useRef(30);
  const textAreaRef = useRef(null);

  if (textAreaRef.current != null amp;amp; textAreaRef.current != undefined) {

    textAreaRef.current.style.height = '0px'; // This creates an inline style

    let scrollHeight = textAreaRef.current.scrollHeight;

    const style = window.getComputedStyle(textAreaRef.current);

    textAreaRef.current.removeAttribute('style'); // The inline style must be removed

    let delta = scrollHeight-lastScrollHeight.current;

    lastScrollHeight.current = scrollHeight;

    idealHeight.current = idealHeight.current   delta;

  }

  return(
    <TextAreaInput
      placeholder={props.placeholder}
      value={props.value}
      onChange={props.onChange}
      ref={textAreaRef}
      idealHeight={idealHeight.current   'px'}
    />
  );

}