React の代替サポート ドリル (逆方向、子から親へ) フォームを処理する方法
<p>私は React を初めて使用しており、いくつかの実践的なプロジェクトを通じて学習しています。現在、フォームの処理と検証に取り組んでいます。 SPA で React Router の Form コンポーネントを使用しています。フォーム内には、ラベル入力とエラー メッセージをレンダリングする FormGroup 要素があります。また、FormGroup コンポーネント内で独自の入力コンポーネントを使用して、フォームで使用される入力のロジックと状態管理を分離します。 </p>
<p>そこで、次のようにサンプル ログイン ページに Form コンポーネントと FormGroup コンポーネントを配置しました。 </p>
<p><em>pages/Login.js</em></p>
<pre class="brush:js;toolbar:false;">import { useState } from 'react';
import { Link, Form, useNavigate, useSubmit } from 'react-router-dom';
FormGroup を '../components/UI/FormGroup' からインポートします。
'../components/UI/Button' からボタンをインポートします。
カードを「../components/UI/Card」からインポートします。
インポート './Login.scss';
関数 LoginPage() {
const navigate = useNavigate();
const submit = useSubmit();
const [isLoginValid, setIsLoginValid] = useState(false);
const [isPasswordValid, setIsPasswordValid] = useState(false);
varresetLoginInput = null;
varresetPasswordInput = null;
isFormValid = false;
if(isLoginValid && isPasswordValid) {
isFormValid = true;
}
関数 formSubmitHandler(event) {
イベント.preventDefault();
if(!isFormValid) {
戻る;
}
リセットログイン入力();
リセットパスワード入力();
submit(event.currentTarget);
}
関数loginValidityChangeHandler(isValid) {
setIsLoginValid(isValid);
}
関数passwordValidityChangeHandler(isValid) {
setIsPasswordValid(isValid);
}
関数resetLoginInputHandler(reset) {
リセットログイン入力 = リセット;
}
関数resetPasswordInputHandler(reset) {
リセットパスワード入力 = リセット;
}
関数 switchToSignupHandler() {
ナビゲート('/サインアップ');
}
戻る (
<div className="ログイン">
<div className="login__logo">
囲碁カップ
</div>
<p className="login__description">
Go Cup アカウントにログインします
</p>
<カード枠>
<フォーム onSubmit={formSubmitHandler}>
<フォームグループ
id="ログイン"
label="ユーザー名または電子メール アドレス"
inputProps={{
タイプ: "テキスト"、
名前: 「ログイン」、
有効性: (値) => {
値 = 値.trim();
if(!値) {
return [false, 'ユーザー名または電子メール アドレスが必要です。']
} else if(値.長さ < 3 || 値.長さ > 30) {
return [false, 'ユーザー名または電子メール アドレスは少なくとも 3 文字、最大 30 文字である必要があります'];
} それ以外 {
戻り値 [true、null];
}
}、
onValidityChange:loginValidityChangeHandler、
onReset:resetLoginInputHandler
}}
/>
<フォームグループ
id="パスワード"
ラベル=「パスワード」
サイドラベル要素={
<リンク先="/password-reset">
パスワードをお忘れですか?
</リンク>
}
inputProps={{
タイプ: 「パスワード」、
名前: "パスワード"、
有効性: (値) => {
値 = 値.trim();
if(!値) {
return [false, 'パスワードが必要です。']
} else if(値.長さ < 4 || 値.長さ > 1024) {
return [false, 'パスワードは 4 文字以上、最大 1024 文字でなければなりません。'];
} それ以外 {
戻り値 [true、null];
}
}、
onValidityChange:passwordValidityChangeHandler、
onReset:resetPasswordInputHandler
}}/>
<div className="text-center">
<ボタンクラス名="w-100" type="送信">
ログイン
</ボタン>
<span className="login__or">
または
</span>
<ボタンクラス名="w-100" onClick={switchToSignupHandler}>
サインアップ
</ボタン>
</div>
</フォーム>
</カード>
</div>
);
}
デフォルトのログインページをエクスポートします。
</pre>
<p>上記のコードでわかるように、FormGroup コンポーネントを使用し、<code>onValidityChange</code> プロパティと <code>onReset</code> プロパティを渡して <code>isValid</ を取得します。 code> value の更新された値。フォーム送信後の入力をリセットする機能の変更・リセット機能等カスタム フック useInput を使用して、入力コンポーネントに <code>isValid</code> 関数と <code>reset</code> 関数を作成します。値が変更されたときに isValid 値を渡し、FormGroup コンポーネントで定義された props を使用して入力コンポーネントからリセット関数を渡します。また、ログイン ページで <code>isLoginValid</code> および <code>isPasswordValid</code> 状態定義を使用して、子の入力から渡された更新された <code>isValid</code> 状態値を保存しています。成分。そこで、入力コンポーネントで状態を定義し、props を使用してそれらを親コンポーネントに渡し、その値をその親コンポーネントで作成された他の状態に保存しました。進行中の支柱の穴あけ作業は少し不快な気分になりました。 </p>
<p>状態は入力コンポーネント内で管理されます。次の状態があります: </p>
<li><strong>値: </strong>要素の値を入力します。 </li>
<li><strong>isInputTouched</strong>: ユーザーが入力をタッチ/フォーカスしたかどうかを判断して、検証エラー メッセージ (存在する場合) を表示するかどうかを決定します。 </li>
</ul>
<p>いくつかの関数 (入力コンポーネントに渡される検証関数など) を組み合わせてこれら 2 つの状態に適用し、他の変数値を作成して、入力とその有効性 (値が有効かどうかなど) に関する情報を収集します。 (isValid)、メッセージ検証 (message) の有無、入力が有効な場合 (<code>isInputValid = isValid || !isInputTouched</code>) で検証メッセージを表示するかどうかを決定します。</p>
<p>これらの状態と値は、次のように、私が作成したカスタム フック <code>useInput</code> で管理されます。
<p><em>hooks/use-state.js</em></p>
<pre class="brush:js;toolbar:false;">import { useState, useCallback } from 'react';
関数 useInput(validityFn) {
const [値, setValue] = useState('');
const [isInputTouched, setIsInputTouched] = useState(false);
const [isValid, message] = typeof validityFn === '関数' ? validityFn(value) : [true, null];
const isInputValid = isValid || !isInputTouched;
const inputChangeHandler = useCallback(event => {
setValue(event.target.value);
if(!isInputTouched) {
setIsInputTouched(true);
}
}, [isInputTouched]);
const inputBlurHandler = useCallback(() => {
setIsInputTouched(true);
}、[]);
constリセット = useCallback(() => {
setValue('');
setIsInputTouched(false);
}、[]);
戻る {
価値、
有効です、
入力有効です、
メッセージ、
inputChangeHandler、
inputBlurHandler、
リセット
};
}
デフォルトの useInput をエクスポートします。
</pre>
<p>現在、Input.js でこのカスタム フックを次のように使用しています。</p>
<p><em>components/UI/Input.js</em></p>
<pre class="brush:js;toolbar:false;">import { useEffect } from 'react';
useInput を '../../hooks/use-input' からインポートします。
インポート './Input.scss';
関数入力(小道具) {
定数{
価値、
有効です、
入力有効です、
メッセージ、
inputChangeHandler、
inputBlurHandler、
リセット
= useInput(props.validity);
定数{
onIsInputValidOrMessageChange、
onValidityChange、
オンリセット
} = 小道具;
let className = 'フォームコントロール';
if(!isInputValid) {
className = `${className} フォームコントロール --invalid`;
}
if(props.className) {
className = `${className} ${props.className}`;
}
useEffect(() => {
if(onIsInputValidOrMessageChange && typeof onIsInputValidOrMessageChange === '関数') {
onIsInputValidOrMessageChange(isInputValid, メッセージ);
}
}, [onIsInputValidOrMessageChange, isInputValid, メッセージ]);
useEffect(() => {
if(onValidityChange && typeof onValidityChange === '関数') {
onValidityChange(isValid);
}
}, [onValidityChange, isValid]);
useEffect(() => {
if(onReset && typeof onReset === '関数') {
onReset(リセット);
}
}, [onReset、リセット]);
戻る (
<入力
{...小道具}
クラス名={クラス名}
値={値}onChange={inputChangeHandler}
onBlur={inputBlurHandler}
/>
);
}
デフォルトの入力をエクスポートします。
</pre>
<p>入力コンポーネントで、<code>isInputValid</code> 状態を直接使用して、無効な CSS クラスを入力に追加します。ただし、<code>isInputValid</code>、<code>message</code>、<code>isValid</code> ステータス、および <code>reset</code> 関数も親コンポーネントに渡します。その中で使用されます。これらの状態と関数を渡すには、プロパティで定義された <code>onIsInputValidOrMessageChange</code>、<code>onValidityChange</code>、<code>onReset</code> 関数を使用します (プロパティはドリルダウンしますが、代わりに、子から親へ)。 </p>
<p>これは FormGroup コンポーネントの定義と、FormGroup 内の入力状態を使用して検証メッセージ (存在する場合) を表示する方法です。 </p>
<p><em>components/UI/FormGroup.js</em></p>
<pre class="brush:js;toolbar:false;">import { useState } from 'react';
'./Input' から入力をインポートします。
インポート './FormGroup.scss';
関数 FormGroup(props) {
const [メッセージ、setMessage] = useState(null);
const [isInputValid, setIsInputValid] = useState(false);
let className = 'フォームグループ';
if(props.className) {
className = `フォームグループ ${props.className}`;
}
labelCmp = (
<label htmlFor={props.id}>
{props.label}
</ラベル>
);
if(props.sideLabelElement) {
labelCmp = (
<div className="フォームラベルグループ">
{ラベルCmp}
{props.sideLabelElement}
</div>
);
}
関数 isInputValidOrMessageChangeHandler(changedIsInputValid,ChangedMessage) {
setIsInputValid(changedIsInputValid);
setMessage(changedMessage);
}
戻る (
<div クラス名={クラス名}>
{ラベルCmp}
<入力
id={props.id}
onIsInputValidOrMessageChange={isInputValidOrMessageChangeHandler}
{...props.inputProps}
/>
{!isInputValid && <p>{メッセージ}</p>}
</div>
);
}
デフォルトの FormGroup をエクスポートします。
</pre>
<p>上記のコードからわかるように、更新された <code>message</code> を保存するために <code>message</code> および <code>isInputValid</code> 状態を定義しました。 <code>isInputValid</code> code> 入力コンポーネントから渡された状態。これらの値を保持するために入力コンポーネントで 2 つの状態を定義しましたが、更新されて渡された値を入力コンポーネントに保存するには、このコンポーネントで別の 2 つの状態を定義する必要があります。これは少し奇妙で、私にとって最善の方法とは思えません。 </p>
<p><strong>質問は次のとおりです: </strong>ここでのプロップドリルの問題を解決するには、React Context (useContext) または React Redux を使用できると思います。しかし、現在の状態管理が悪く、React Context または React Redux を使用して改善できるかどうかはわかりません。なぜなら、私が理解しているところによると、React Context は状態が頻繁に変化する状況ではひどいものになる可能性がありますが、Context がアプリケーション全体で使用されている場合は機能するからです。ここで、フォーム全体を保存および更新するためのコンテキストを作成し、フォーム全体の拡張を可能にします。一方、React Redux はサイロに最適ではない可能性があり、少しやりすぎである可能性があります。どう思いますか?この特定の状況に対して、より良い代替手段は何でしょうか? </p>
<p><strong>注: </strong>私は React を初めて使用するため、単純な間違いから一般的な間違いまで、すべてのコーディングに関するあらゆる提案を歓迎します。ありがとう! </p>