<textarea>
ブラウザ組み込みの <textarea>
コンポーネントを利用することで、複数行のテキスト入力エリアをレンダーすることができます。
<textarea />
リファレンス
<textarea>
テキストエリアを表示するためには、ブラウザ組み込みの <textarea>
コンポーネントをレンダーします。
<textarea name="postContent" />
props
<textarea>
は一般的な要素の props をすべてサポートしています。
value
プロパティを渡すことで、テキストエリアを制御されたコンポーネント (controlled component) にできます。
value
: 文字列。テキストエリア内のテキストを制御します。
value
を渡す場合は、渡された値を更新する onChange
ハンドラも渡す必要があります。
もし <textarea>
を非制御コンポーネント (uncontrolled component) として使用する場合は、代わりに defaultValue
プロパティを渡すことができます。
defaultValue
: 文字列。テキストエリアの初期値を指定します。
これらの <textarea>
の props は、非制御のテキストエリアと制御されたテキストエリアの両方で用いられます。
autoComplete
:'on'
または'off'
。オートコンプリートの挙動を指定します。autoFocus
: ブーリアン。true
の場合、React はマウント時にこの要素にフォーカスします。children
:<textarea>
には子要素を指定できません。初期値を設定するにはdefaultValue
を使用します。cols
: 数値。デフォルトの幅を平均文字幅で指定します。デフォルトは20
です。disabled
: ブーリアン。true
の場合、入力エリアは操作不可能になり、表示が暗くなります。form
: 文字列。この入力が属する<form>
のid
を指定します。省略された場合、最も近い親のフォームとなります。maxLength
: 数値。テキストの最大長を指定します。minLength
: 数値。テキストの最小長を指定します。name
: 文字列。フォームで送信されるこの入力エリアの名前を指定します。onChange
:Event
ハンドラ 関数。制御されたテキストエリアでは必須。ユーザによって入力値が変更されるとすぐに発火します(例えば、各キーストロークで発火します)。ブラウザのinput
イベント と同様に動作します。onChangeCapture
:onChange
のキャプチャフェーズで発火するバージョン。onInput
:Event
ハンドラ関数。ユーザによって値が変更されるとすぐに発火します。歴史的な理由から、React では同様に動作するonChange
を使用するのが慣例です。onInputCapture
:onInput
のキャプチャフェーズで発火するバージョン。onInvalid
:Event
ハンドラ関数。フォームの送信時に入力が検証に失敗した場合に発火します。組み込みのinvalid
イベントとは異なり、React のonInvalid
イベントはバブリングします。onInvalidCapture
:onInvalid
のキャプチャフェーズで発火するバージョン。onSelect
:Event
ハンドラ関数。<textarea>
内で選択テキストが変更された後に発火します。React はonSelect
イベントを拡張しており、空の選択やテキストの編集(選択に影響を与える可能性がある)でも発火します。onSelectCapture
:onSelect
のキャプチャフェーズで発火するバージョン。placeholder
: 文字列。テキストエリアの値が空の場合、これが薄い色で表示されます。readOnly
: ブーリアン。true
の場合、テキストエリアはユーザによって編集できなくなります。required
: ブーリアン。true
の場合、フォームを送信するためには値が必須となります。rows
: 数値。デフォルトの高さを平均文字高での指定します。デフォルトは2
です。wrap
:'hard'
、'soft'
、または'off'
。フォームを送信するときにテキストがどのように折り返されるかを指定します。
注意点
<textarea>something</textarea>
のように子要素を渡すことはできません。初期内容の指定にはdefaultValue
を使用してください。- テキストエリアが props として文字列型の
value
を受け取ると、制御されたテキストエリアとして扱われます。 - テキストエリアは制御されたコンポーネントと非制御コンポーネントに同時になることはできません。。
- テキストエリアは、ライフタイム中に制御されたコンポーネントから非制御コンポーネント、またはその逆に切り替えることはできません。
- すべての制御されたテキストエリアには、制御に使っている state を同期的に更新するための
onChange
イベントハンドラが必要です。
使用法
テキストエリアを表示する
テキストエリアを表示するには、<textarea>
をレンダーします。そのデフォルトのサイズは rows
と cols
属性で指定できますが、デフォルトではユーザがそれをリサイズできます。リサイズを無効にするには、CSS で resize: none
を指定します。
export default function NewPost() { return ( <label> Write your post: <textarea name="postContent" rows={4} cols={40} /> </label> ); }
テキストエリアにラベルを付ける
通常、すべての <textarea>
は <label>
タグ内に配置します。これにより、ブラウザに対してこのラベルがそのテキストエリアに関連付けられていることが伝わります。ユーザがラベルをクリックすると、ブラウザは自動的にテキストエリアにフォーカスします。これはアクセシビリティの観点からも重要です。ユーザがテキストエリアにフォーカスすると、スクリーンリーダがラベルのキャプションを読み上げます。
もし <label>
内に <textarea>
をネストできない場合は、同じ ID を <textarea id>
と <label htmlFor>
に渡すことで関連付けることができます。同一コンポーネントの複数のインスタンス間での競合を避けるために、useId
を使用してそのような ID を生成してください。
import { useId } from 'react'; export default function Form() { const postTextAreaId = useId(); return ( <> <label htmlFor={postTextAreaId}> Write your post: </label> <textarea id={postTextAreaId} name="postContent" rows={4} cols={40} /> </> ); }
export default function EditPost() { return ( <label> Edit your post: <textarea name="postContent" defaultValue="I really enjoyed biking yesterday!" rows={4} cols={40} /> </label> ); }
フォーム送信時にテキストエリアから値を読み取る
テキストエリアを <form>
で囲み、その中に <button type="submit">
を配置します。これにより、<form onSubmit>
イベントハンドラが呼び出されます。デフォルトでは、ブラウザはフォームデータを現在の URL に送信し、ページを更新します。e.preventDefault()
を呼び出すことで、その振る舞いをオーバーライドできます。new FormData(e.target)
を使用してフォームデータを読み込みます。
export default function EditPost() { function handleSubmit(e) { // Prevent the browser from reloading the page e.preventDefault(); // Read the form data const form = e.target; const formData = new FormData(form); // You can pass formData as a fetch body directly: fetch('/some-api', { method: form.method, body: formData }); // Or you can work with it as a plain object: const formJson = Object.fromEntries(formData.entries()); console.log(formJson); } return ( <form method="post" onSubmit={handleSubmit}> <label> Post title: <input name="postTitle" defaultValue="Biking" /> </label> <label> Edit your post: <textarea name="postContent" defaultValue="I really enjoyed biking yesterday!" rows={4} cols={40} /> </label> <hr /> <button type="reset">Reset edits</button> <button type="submit">Save post</button> </form> ); }
state 変数を使ってテキストエリアを制御する
<textarea />
のようなテキストエリアは非制御です。たとえ <textarea defaultValue="Initial text" />
のようにデフォルト値を指定している場合でも、この JSX で指定しているのはあくまで初期値であって現在の値ではありません。
制御されたテキストエリアをレンダーするには、value
プロパティを渡してください。React はテキストエリアが常に渡した value
を反映するようにします。通常、state 変数を宣言することでテキストエリアを制御します。
function NewPost() {
const [postContent, setPostContent] = useState(''); // Declare a state variable...
// ...
return (
<textarea
value={postContent} // ...force the input's value to match the state variable...
onChange={e => setPostContent(e.target.value)} // ... and update the state variable on any edits!
/>
);
}
これは、キーストロークごとに UI の一部を再レンダーしたい場合に便利です。
{ "dependencies": { "react": "latest", "react-dom": "latest", "react-scripts": "latest", "remarkable": "2.0.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "devDependencies": {} }
トラブルシューティング
テキストエリアにタイプしても内容が更新されない
value
があるが onChange
のないテキストエリアをレンダーすると、コンソールにエラーが表示されます。
// 🔴 Bug: controlled text area with no onChange handler
<textarea value={something} />
value
prop to a form field without an onChange
handler. This will render a read-only field. If the field should be mutable use defaultValue
. Otherwise, set either onChange
or readOnly
.エラーメッセージが示すように、初期値を指定したいだけの場合は、代わりに defaultValue
を渡すようにしてください。
// ✅ Good: uncontrolled text area with an initial value
<textarea defaultValue={something} />
state 変数でこのテキストエリアを制御したい場合は、onChange
ハンドラを指定してください。
// ✅ Good: controlled text area with onChange
<textarea value={something} onChange={e => setSomething(e.target.value)} />
値を意図的に読み取り専用にしたい場合は、エラーを抑制するために props として readOnly
を追加してください。
// ✅ Good: readonly controlled text area without on change
<textarea value={something} readOnly={true} />
キーストロークごとにテキストエリアのカーソルが先頭に戻る
テキストエリアを制御する場合、onChange
中でその state 変数を DOM からやってくるテキストエリアの値に更新する必要があります。
state を e.target.value
以外のものに更新することはできません。
function handleChange(e) {
// 🔴 Bug: updating an input to something other than e.target.value
setFirstName(e.target.value.toUpperCase());
}
また、非同期に更新することもできません。
function handleChange(e) {
// 🔴 Bug: updating an input asynchronously
setTimeout(() => {
setFirstName(e.target.value);
}, 100);
}
コードを修正するには、state を e.target.value
の値に同期的に更新します。
function handleChange(e) {
// ✅ Updating a controlled input to e.target.value synchronously
setFirstName(e.target.value);
}
これで問題が解決しない場合、テキストエリアがキーストロークごとに DOM から削除・再追加されている可能性があります。これは、再レンダーごとに state を誤ってリセットしている場合に起こります。例えば、テキストエリアまたはその親が常に異なる key
属性を受け取っている可能性や、コンポーネント定義をネストしている(これは React では許されておらず、「内側」のコンポーネントがレンダー時に再マウントさえることになります)可能性があります。
“A component is changing an uncontrolled input to be controlled” というエラーが発生する
コンポーネントに value
を渡す場合、そのライフサイクル全体を通じて文字列型でなければなりません。
最初に value={undefined}
を渡しておき、後で value="some string"
を渡すようなことはできません。なぜなら、React はあなたがコンポーネントを非制御コンポーネントと制御されたコンポーネントのどちらにしたいのか分からなくなるからです。制御されたコンポーネントは常に文字列の value
を受け取るべきであり、null
や undefined
であってはいけません。
あなたの value
が API や state 変数から来ている場合、それが null
や undefined
に初期化されているかもしれません。その場合、まず空の文字列(''
)にセットするか、value
が文字列であることを保証するために value={someValue ?? ''}
を渡すようにしてください。