この記事では、React
とMaterial UI
を用いて作った入力フォームにバリデーションチェックを実装する。
Reactをインストール
下記コマンドを入力して実行し、適切なプロジェクト名を入力し、ReactとJavaScriptを選択します。
npm create vite
作成されたフォルダに入り、下記コマンドで必要なパッケージをインストールします。node_moduleフォルダが作成されます。
npm install
バリデーションを実装する
下記コマンドを実行し、http://localhost:5173/
にアクセスするとデフォルトの画面が立ち上がります。
npm run dev
srcフォルダ内のApp.jsxの中身を下記のように不要なものを一旦削除します。
function App() {
return (
<>
</>
)
}
export default App
App.cssファイルとindex.cssファイル内の内容をすべて削除します。真っ白な画面になります。
ここで、Material UIパッケージをインストールします。ターミナルをもう一つ立ち上げて、Reactフォルダ内で、Material UIのドキュメントを参考にして下記コマンドを入力し実行します。
npm install @mui/material @emotion/react @emotion/styled
次に、MaterialドキュメントのTextFieldに関連するページを参考にして、App.jsxにTextFieldを使ってみます。
import TextField from '@mui/material/TextField'
function App() {
return (
<>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>username: </p>
<TextField
error={true} // textFieldが赤くなります
helperText="5~10文字入力してください" // 文字が表示されます
size='small'
/>
</div>
</>
)
}
export default App
画面が下記のようになります。<TextField>
のerror属性がtrueの場合、入力欄が赤色になります。また、helperText属性に文字が存在する場合も、ちゃんと表示してくれるようです。
ということで、入力した内容により<TextField>の属性を変化させればよさそうです。
同じように、パスワード入力欄とログインボタンを簡単に作ってみます。
import TextField from '@mui/material/TextField'
function App() {
return (
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>username: </p>
<TextField
error={true} // textFieldが赤くなります
helperText="5~10文字入力してください" // 文字が表示されます
size='small'
/>
</div>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>password: </p>
<TextField
error={true} // textFieldが赤くなります
helperText="8~16文字入力してください" // 文字が表示されます
size='small'
/>
</div>
<button style={{marginTop: '30px'}}>ログイン</button>
</div>
)
}
export default App
次に入力内容に応じて<TextField>の属性を変化させる部分について考えます。
まずusernameの入力欄に文字が入力されるたびに条件を満たすかを判定したいので、<TextField>にonChange属性を追加します。関数名をnameHandleChangeとします。
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>username: </p>
<TextField
error={true}
helperText="5~10文字入力してください"
size='small'
onChange={nameHandleChange}
/>
</div>
nameHandleChange関数内では、入力された内容を取得し、正規表現を用いて条件に一致するかを判断します。さらに返されたBoolean型の結果で<TextField>の属性をコントロールしたいので、この結果をuseStateを使って管理します。
const [nameError, setNameError] = useState(false) // usernameの入力が正しいかを保存する
const nameHandleChange = event => {
const pattern = /^\w{5,10}$/g // 5~10文字か
const res = pattern.test(event.target.value) // event.target.value: textFieldに入力された内容
setNameError(!res) // 結果をnameErrorに保存する
}
ここでsetNameError(!res)
において、!res
をnameErrorに保存しているのは、正規表現の結果がfalseの場合にnameErrorをtrueにしたい為です。
つぎにnameErrorの値によって<TextField>の表示を変化させたいので、usernameの<TextField>を下記のように修正します。
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>username: </p>
<TextField
error={nameError}
helperText={nameError ? "5~10文字入力してください" : ''}
size='small'
onChange={nameHandleChange}
/>
</div>
そうすると下記のようになります。
パスワード入力欄も同じように実装します。全体のコードが下記のようになります。
import TextField from '@mui/material/TextField'
import { useState } from 'react'
function App() {
const [nameError, setNameError] = useState(false) // usernameの入力が正しいかを保存する
const [passError, setPassError] = useState(false) // passwordの入力が正しいかを保存する
const nameHandleChange = event => {
const pattern = /^\w{5,10}$/g // 5~10文字か
const res = pattern.test(event.target.value) // event.target.value: textFieldに入力された内容
setNameError(!res) // 結果をnameErrorに保存する
}
const passHandleChange = event => {
const pattern = /^\w{8,16}$/g // 8~16文字か
const res = pattern.test(event.target.value) // event.target.value: textFieldに入力された内容
setPassError(!res) // 結果をpassErrorに保存する
}
return (
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>username: </p>
<TextField
error={nameError}
helperText={nameError ? "5~10文字入力してください" : ''}
size='small'
onChange={nameHandleChange}
/>
</div>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>password: </p>
<TextField
error={passError}
helperText={passError ? "8~16文字入力してください" : ''}
size='small'
onChange={passHandleChange}
/>
</div>
<button style={{marginTop: '30px'}}>ログイン</button>
</div>
)
}
export default App
最後にログインボタンを押した際、入力形式が正しければ'OK!'と出力し(実際のアプリでは、バックエンドに処理を投げる)、そうでなければ'NO!'と出力するようにコードを追加します。
<button>
のonClick属性に、handleClick関数を追加します。handleClick関数内では、nameErrorおよびpassErrorの値によって出力を変えます。
function App() {
// ......省略
const handleClick = () => {
if (!nameError && !passError) {
console.log('OK!')
} else {
console.log('NO!')
}
}
return (
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
{/* ......省略 */}
<button onClick={handleClick} style={{marginTop: '30px'}}>ログイン</button>
</div>
)
}
これでおおむね完了ですが、nameErrorとpassErrorは初期値としてfalseを設定していますので、最初の画面で何も入力しない状態でも、!nameError && !passError
がtrueになってしまいます。ここでは、useRef
フックを使ってTextField内が空かどうかを確認します。
import TextField from '@mui/material/TextField'
import { useRef, useState } from 'react'
function App() {
const usernameRef = useRef('') // username入力欄への参照
const passwordRef = useRef('') // password入力欄への参照
const [nameError, setNameError] = useState(false) // usernameの入力が正しいかを保存する
const [passError, setPassError] = useState(false) // passwordの入力が正しいかを保存する
const nameHandleChange = event => {
const pattern = /^\w{5,10}$/g // 5~10文字か
const res = pattern.test(event.target.value) // event.target.value: textFieldに入力された内容
setNameError(!res) // 結果をnameErrorに保存する
}
const passHandleChange = event => {
const pattern = /^\w{8,16}$/g // 8~16文字か
const res = pattern.test(event.target.value) // event.target.value: textFieldに入力された内容
setPassError(!res) // 結果をpassErrorに保存する
}
const handleClick = () => {
const username = usernameRef.current.value // usernameRefの現在の値を取得
const password = passwordRef.current.value // passwordRefの現在の値を取得
// username, passwordに値が入っていて、かつnameErrorとpassErrorがfalseの場合
if (username && password && !nameError && !passError) {
console.log('OK!')
} else {
console.log('NO!')
}
}
return (
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>username: </p>
<TextField
error={nameError}
helperText={nameError ? "5~10文字入力してください" : ''}
size='small'
onChange={nameHandleChange}
inputRef={usernameRef} // useRefでusername入力欄への参照
/>
</div>
<div style={{margin: '20px', display: 'flex', alignItems: 'center', gap: '20px'}}>
<p>password: </p>
<TextField
error={passError}
helperText={passError ? "8~16文字入力してください" : ''}
size='small'
onChange={passHandleChange}
inputRef={passwordRef} // useRefでpassword入力欄への参照
/>
</div>
<button onClick={handleClick} style={{marginTop: '30px'}}>ログイン</button>
</div>
)
}
export default App
これでフロント側でのバリデーションチェック機能を実現できました。