Попытка создать компонент React с textarea, который автоматически расширяется и сжимается в зависимости от высоты содержимого (с помощью Sandbox) - PullRequest
1 голос
/ 03 апреля 2019

Я пытаюсь построить этот 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'.Я не знаю.

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

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

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 && 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 Ответ

1 голос
/ 04 апреля 2019

Только что придумали решение:

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

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

textAreaRef.current.removeAttribute('style');

Это необходимо, потому что весь мой CSS применяется стилевыми компонентами, которые используют тег <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 && 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'}
    />
  );

}
...