這次我們使用 Custom hook 進行表單的驗證。
首先我們建立一個檔案叫 useInputValidate.js ,裡面將會寫一個 Custom hook,分兩個部分完成。
先寫出 hook 的函式,並產生 state,這裡使用 useReducer 管理 state。
此 hook 接受一個參數 validateValue,因為不同的輸入內容驗證輸入有效的方式也會不同,所以就把驗證的函式做為參數傳入。
const initialInput = {
value: '',
isTouched: false,
};
const inputReducer = (state, action) => {
switch (action.type) {
case "INPUT":
return { value: action.value, isTouched: state.isTouched };
case "BLUR":
return { isTouched: true, value: state.value };
case "Reset":
return { isTouched: false, value: '' };
default:
return state;
}
}
const useInputValidate = (validateValue) => {
const [input, dispatch] = useReducer(inputReducer, initialInput);
}
加上相關的事件函式及回傳值就完成此 hook。
const initialInput = {
value: "",
isTouched: false
};
const inputReducer = (state, action) => {
switch (action.type) {
case "INPUT":
return { value: action.value, isTouched: state.isTouched };
case "BLUR":
return { isTouched: true, value: state.value };
case "Reset":
return { isTouched: false, value: "" };
default:
return state;
}
};
const useInputValidate = (validateValue) => {
const [input, dispatch] = useReducer(inputReducer, initialInput);
const valueIsValid = validateValue(input.value);
const hasError = !valueIsValid && input.isTouched;
const onChangeValue = (e) => {
dispatch({ type: "INPUT", value: e.target.value });
};
const onBlurValue = () => {
dispatch({ type: "BLUR" });
};
const reset = () => {
dispatch({ type: "RESET" });
};
return {
value: input.value,
isValid: valueIsValid,
hasError,
onChangeValue,
onBlurValue,
reset
};
};
接著我們來實際使用一下這個 hook,將 hook 回傳的內容進行解構賦值,加到會用到它們的地方即可。
另外記得給 hook 一個函式當作參數,用來驗證輸入值用。
import "./styles.css";
import useInputValidate from "./useInputValidate";
const emailRule = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z]+$/;
const isNotEmpty = (value) => value.trim() !== "";
const isEmailFormat = (value) => emailRule.test(value);
const SimpleForm = () => {
const {
value: name,
isValid: nameIsValid,
hasError: nameError,
onChangeValue: onChangeName,
onBlurValue: onBlurName,
reset: resetName
} = useInputValidate(isNotEmpty);
const {
value: email,
isValid: emailIsValid,
hasError: emailError,
onChangeValue: onChangeEmail,
onBlurValue: onBlurEmail,
reset: resetEmail
} = useInputValidate(isEmailFormat);
const onFormSubmit = (e) => {
e.preventDefault();
if (!nameIsValid || !emailIsValid) {
return;
}
console.log("submit success!");
console.log(name, email);
// reset
resetName();
resetEmail();
};
const nameInputClasses = nameError ? "invalid" : "";
const emailInputClasses = emailError ? "invalid" : "";
return (
<form onSubmit={onFormSubmit}>
<label htmlFor="name">Your Name</label>
<input
type="text"
id="name"
onChange={onChangeName}
onBlur={onBlurName}
value={name}
className={nameInputClasses}
/>
{nameError && <p className="error-text">Name must not be empty.</p>}
<label htmlFor="email">Your E-Mail</label>
<input
type="email"
id="email"
onChange={onChangeEmail}
onBlur={onBlurEmail}
value={email}
className={emailInputClasses}
/>
{emailError && <p className="error-text">Please enter a valid email.</p>}
<button disabled={!nameIsValid || !emailIsValid}>Submit</button>
</form>
);
};
export default SimpleForm;
透過以上的方式,如果表格欄位一多的時候,就不用再寫許多相似的程式碼,讓整個表單元件的程式碼變得更加可讀。