自動で伸縮する textarea コンポーネント
はじめに
入力に合わせてサイズが変わるテキストエリアは、MDN にあるように Element.scrollHeight で要素の高さを取得しこれを HTMLElement.style.height にセットすることで実装できる。 (MDN の例は oField.clientHeight < oField.scrollHeight によりテキストを削除してもサイズは小さくならない。)
CSS の設定も必要で、overflow-y: hidden; を書かないとスクロールバーが出てしまい正しく動作しない。
これを React コンポーネントでやる場合の記事がなかったのと、textarea.style.height は更新する前にリセットしないと正しく動作しないというのに嵌ったので、実装の様子をメモとして残しておく。
実装の様子
基本的には、素の JS でやる場合と同様に textarea.scrollHeight を textarea.style.height にセットするだけだが、React でやる場合はこの処理の前に textarea.style.height をリセットする必要がある。
これにより正しく伸縮が行われ、コンポーネントの最小の高さを指定することも可能になる。
code:AutoHeightTextarea.tsx
import React, { useCallback, useEffect, useRef, useState } from "react";
type Props = {
minHeight: number;
};
const AutoHeightTextarea: React.FC<Props> = ({ minHeight }) => {
const ref = useRef<HTMLTextAreaElement>(null);
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const textarea = ref.current!;
textarea.style.height = ${minHeight}px; // 必須, これより小さくならない
textarea.style.height = ${textarea.scrollHeight}px;
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setContent(e.target.value);
},
[]
);
return (
<textarea
className="auto-height-textarea"
value={content}
onChange={handleChange}
ref={ref}
/>
);
};
code:style.css
.auto-height-textarea {
overflow-y: hidden; /* 必須 */
overflow-wrap: break-word;
resize: none;
}
rows でやる
code:ts
type Props = {
initialRows?: number;
maxLength?: number;
};
const AutoHeightTextarea: React.FC<Props> = ({
initialRows = 1,
maxLength,
}) => {
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.currentTarget.value;
if (maxLength && value.length > maxLength) return;
setContent(value);
},
[],
);
useEffect(() => {
const rows = content.split("\n").length;
setRows(rows > initialRows ? rows : initialRows);
return (
<textarea
value={content}
onChange={handleChange}
rows={rows}
style={{ resize: "none" }}
/>
);
};
参考 URL