Reactジャンプ率ジェネレーターの続きです。前回はAboutページ作成とinput(type="range")タグの配置を行いました。しかしinputタグに初期値value
を入れたらツマミが動かなくなりました。今回はこのツマミを動かすべく、onChange
設定をしたいきたく思います。それではいきましょう!
【目次】
- 前回のおさらい
- onChangeとは
- 関数コンポーネントでonChangeを設定
- Reactの値はステートで制御する
- e.target.valueを取得してみる
- フックでステートの値を更新する
- ブラウザ挙動
- 最後に
※参考:前回記事
【React】Aboutページ作成、input(type="range")タグ配置(ジャンプ率ジェネレーター) - クモのようにコツコツと
※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com
前回のおさらい
Otherページを改造してAboutページ作成。ルーティング設定も変更した。
メインページにinput(type="range")タグを配置。しかしinputタグのツマミがまだ動かない。初期値value
を入れたため。
これを動かすにはonChange
設定が必要なようだ。
※参考:【React】Aboutページ作成、input(type="range")タグ配置(ジャンプ率ジェネレーター) - クモのようにコツコツと
onChangeとは
コンソールには下記のエラーメッセージ
Warning: You provided a `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`.
警告: onChangeハンドラーなしでフォームフィールドにvalueプロパティを指定しました。 これにより、読み取り専用フィールドがレンダリングされます。 フィールドを変更可能にする必要がある場合は、 defaultValueを使用します。 それ以外の場合は、 onChangeまたはreadOnlyのいずれかを設定します。
onChange
については以前、こちらでもやったReactのイベント設定。関数コンポーネントで設定している。
※参考:【React】イベント属性を使ってみた(onChangeとonClick) - クモのようにコツコツと
Reactのドキュメントではクラスコンポーネントの事例になっている。
※参考:フォーム – React
ドキュメントの「独自フック」のところでonChangeの事例があるのでフックを使えば関数コンポーネントでも問題なさそう。
※参考:独自フックの作成 – React
クラスコンポーネントと関数コンポーネントの比較記事もあった♪
※参考:React Hooksに対応した複数のonChangeハンドラを処理する書き方 | 会津ラボブログ
関数コンポーネントでonChangeを設定
最初にinputタグにダメ元でonChange={}
(中身は空)を書いてみると
<InputRange type="range" name="range" min="10" max="50" value="35" onChange={}></InputRange>
当然ながらエラーになる
SyntaxError: /(パス)/react_app/jump-rate-generator/src/Inner.tsx: JSX attributes must only be assigned a non-empty expression (18:86)
JSX属性には、空でない式のみを割り当てる必要があります
ふむ、そうだよね。
次に架空(未設定)の関数名test
を入れてみる
<InputRange type="range" name="range" min="10" max="50" value="35" onChange={test}></InputRange>
まあ当然まだエラー
TypeScript error in /(パス)/react_app/jump-rate-generator/src/Inner.tsx(18,78): No overload matches this call.
この呼び出しに一致する過負荷はありません。
関数test()
を作ってみる。
const test = () => { console.log('動いた!'); };
コンソールに「動いた!」と表示するだけ。
お、エラーが消えた!
ツマミを動かすとコンソールの「動いた!」の数が増える!
しかし肝心のツマミの位置はまだ動かない。初期値が変わらないからだ…
とりあえずonChange
で関数を実行するとエラーが消えることだけはわかった。
Reactの値はステートで制御する
再びReactドキュメントのフォームのところを読むと
HTML では
<input>
、<textarea>
、そして<select>
のようなフォーム要素は通常、自身で状態を保持しており、ユーザの入力に基づいてそれを更新します。React では、変更されうる状態は通常はコンポーネントの state プロパティに保持され、setState() 関数でのみ更新されます。React の state を “信頼できる唯一の情報源 (single source of truth)” とすることで、上述の 2 つの状態を結合させることができます。そうすることで、フォームをレンダーしている React コンポーネントが、後続するユーザ入力でフォームで起きることも制御できるようになります。このような方法で React によって値が制御される入力フォーム要素は「制御されたコンポーネント」と呼ばれます。
※参考:フォーム – React
おお、たぶんここ大事なところ。通常のフォーム要素は自分自身で状態を保持するが、Reactではstate
で状態を保持し、setState()
のみで更新すると。
state
のドキュメントにはこうある
setState() はコンポーネントの state オブジェクト更新をスケジュールします。state が更新されると、コンポーネントはそれに再レンダーで応じます。
そして以前、自分もstate
プロパティやsetState()
関数の設定をステートの記事の時にやっている。このときはonClick
イベントだがやってることは同じはず。
※参考:【React】Reactプロジェクトでステートを事始め(setState()、タイマー処理、イベント処理) - クモのようにコツコツと
で、そのステート設定を関数コンポーネントで実現するためにフックを使う、と。この時もonClick
イベントで設定してるな。
※参考:【React】フック(React Hooks)事始め:useState、useEffect、useContext - クモのようにコツコツと
で、先ほどの「独自フック」のドキュメントではonChange
を設定している。
※参考:独自フックの作成 – React
e.target.valueを取得してみる
フックを使うまえにもう一つ試してみたいこと。e.target.value
を取得するとどうなるか。
const test = (e) => { const defaultValue = e.target.value; console.log(defaultValue); };
- 引数
e
でイベントを取得 - 変数
defaultValue
でe.target.value
を取得 - コンソールで表示
おっと、TypeScriptに怒られた。
TypeScript error in /(パス)/react_app/jump-rate-generator/src/Inner.tsx(11,15): Parameter 'e' implicitly has an 'any' type. TS7006
「パラメータ「e」は暗黙的に「any」型を持っています。 TS7006」
まあ困ったときにはany
型を付ければエラーは消えるのだが…
const test = (e: any) => { const defaultValue = e.target.value; console.log(defaultValue); };
他にふさわしい型はあるのかな。
anyを使うのはちょっと気が引けるということであれば
event: React.ChangeEvent<HTMLInputElement>
※参考:TypeScript - react eventHandlerのエラーが解消できない|teratail
お?React.ChangeEvent<HTMLInputElement>
とはなんぞや。
こちらの記事のようにVSCodeでonChange
にマウスオーバーすると表示される
※参考:any型で諦めない React.EventCallback - Qiita
「@types/react」の中で定義されているようだ。
型をReact.ChangeEvent<HTMLInputElement>
に変更してみる!
const test = (e: React.ChangeEvent<HTMLInputElement>) => { const defaultValue = e.target.value; console.log(defaultValue); };
おお、エラーはなくなった!range
のツマミを動かすとコンソールに変更されたvalue
の値が表示されている!
フックでステートの値を更新する
いよいよフックを使ってみるか。
useState
をインポート
import React, { useState } from 'react';
フックを設定
const [lineLength, setLineLength] = useState(35);
- 変数
lineLength
とsetLineLength
にフックを設定、useState()
で初期値は35
に
setLineLength
の値変更設定
const changelineLength = (e: React.ChangeEvent<HTMLInputElement>) => { let changeValue: number = Number(e.target.value); console.log(changeValue); setLineLength(changeValue); };
changelineLength
はアロー関数、引数はe
(イベント)、e
の型はReact.ChangeEvent<HTMLInputElement>
- 変数
changeValue
の型はnumber
でe.target.value
を取得(Number()
で文字列を数値に変換) setLineLength()
の引数でchangeValue
を取得
e.target.value
の値は文字列だった。そのままだとTypeScriptエラーになるのでNumber()
で数値に変換した。
JSX部分にフックを反映
<section> <h2>行長</h2> <p>値:{lineLength}</p> <InputRange type="range" name="range" min="10" max="50" defaultValue={lineLength} onChange={changelineLength}></InputRange> </section> <section>
- pタグを追加し「値:」のあとに
lineLength
を埋め込む - inputタグの
value
属性をdefaultValue
属性に変更し、値にlineLength
を埋め込む
onChange
イベントでchangelineLength()
を実行
value
のままだとrange
のツマミが固定されて動かなかった。
Reactではvalue
の代わりにdefaultValue
を使うことで初期値を更新できる。
ブラウザ挙動
一つ目の「行長」のところ、ページをロードすると初期値の「35」になっている。文字も表示されている。
ツマミを右に動かす。ちゃんと動いた!値表示も「41」に増えた
今後はツマミを左に動かす。値表示が「21」に減った
コード(GitHub)
※参考:GitHub - ryo-i/jump-rate-generator at 11c227cba0639eee7224fc309b45ad15f20322de
プレビュー(GitHub Pages)
※参考:React App
最後に
ようやくinputタグのrange
のツマミが動くようになりましたー。過去に自分が経験してきたはずのステートやフック設定などの復習にもなり、また点と点をつなげて線になるのは自分の中での理解が深まって嬉しいことです。
まだ、動きとしてはinputの値を変更したり文字として表示しているだけですが、これからのこの値をCSSスタイルに反映したりして、ジャンプ率ジェネレーターの形に近づけていきたいと思います。
それではまた!
※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com