Walidacja formularza w React
19.06.2022 | aktual.: 20.06.2022 08:11
Walidacja formularza jest przeprowadzona za pomocą mojego hooka useForm. Obsługiwana jest walidacja:
- required – sprawdzane jest czy pole formularza zawiera jakieś dane
- isEmail – sprawdzane jest czy wpisane dane są poprawnym adresem email
- min – sprawdzane jest czy wprowadzone dane mają odpowiednią liczbę znaków
- match – sprawdzane jest czy wprowadzone dane są takie same jak w innym polu formularza (czy hasło i powtórzenie hasła są takie same)
Aby użyć hook w komponencie zawierającym formularz należy dodać wpis:
const {values, errors, validate, handleSubmit} = useForm(callback)
Przekazane z hooka wartości to:
- values – obiekt zawierający wartości wprowadzone do formularza i (po walidacji) przekazane do funkcji callback(), w której jest dokonywane zapytanie do zewnętrznego API
- errors – obiekt zawierający wartości błędów walidacji, które będą wyświetlane pod polami formularza
- validate – funkcja dokonująca walidacji wpisanych wartości podczas pisania
- handleSubmit – funkcja dokonująca walidacji podczas wysyłania formularza (onSubmit)
<form onSubmit={(event) => handleSubmit(event, ['role', 'username', 'email', 'pass', 'passConfirm'])}>
Przykładowy element formularza o nazwie passConfirm wygląda następująco:
<div> <input name='passConfirm' placeholder='Retype Password' type='password' value={values.passConfirm || ''} onChange={(event) => validate( event, {'match' : 'pass', 'required': true})}/> <p className='help is-danger'>{errors.passConfirm}</p> </div>
Hook useForm.js:
import { useState } from 'react' function useForm(callback) { const [values, setValues] = useState({}) const [errors, setErrors] = useState({}) const pattern = new RegExp( /^[a-zA-Z0-9]+@(?:[a-zA-Z0-9]+\.)+[A-Za-z]+$/ )
Na wstępie inicjowane są obiekty: values – przechowujący wprowadzone wartości do pól formularza oraz errors – przechowujący komunikaty błędów. Następnie tworzony jest obiekt RegExp przechowujący wzorzec pasujący do prawidłowego adresu email.
Funkcja useForm pobiera jako argument nazwę funkcji zwrotnej, która będzie wywołana po przesłaniu formularza i wykonaniu funkcji handleSubmit:
const handleSubmit = (event, controls) => { event.preventDefault() controls.map((value) => validateOnSubmit(value)) setErrors({...errors}) if (!Object.keys(errors).length) { callback() } }
Powyższa funkcja pobiera jako argumenty obiekt event oraz listę pól formularza. Następnie wywoływana jest funkcja preventDefault() zapobiegająca domyślnemu działaniu podczas przesłania formularza, a dalej wywoływana jest funkcja validateOnSubmit() dla każdego pola formularza tzn.
const validateOnSubmit = (value) => { if (values[value] === undefined) { errors[[value]] = 'This field is required' } }
Powyższa funkcja sprawdza, czy w polu formularza są wprowadzone jakieś wartości. Jeśli nie generowany jest błąd.
Jeśli obiekt errors nie będzie pusty, tzn. będą obecne błędy, to nie nastąpi wykonanie funkcji zwrotnej.
Funkcja validate() dokonuje sprawdzania wprowadzonego tekstu do pola formularza podczas pisania, a także wyświetla odpowiednie komunikaty o błędach walidacji podczas pisania. Pobiera jako argumenty obiekt event oraz obiekt zawierający reguły walidacji.
const validate = (event, rules) => { setValues(values => ({...values, [event.target.name]: event.target.value}))
Dla dowolnego pola formularza wywołana jest funkcja setValues(), która uzupełnia obiekt values o obiekt o kluczu pola formularza, do którego wprowadza się aktualnie tekst. Następnie wywoływane są poszczególne walidacje dla określonych reguł, tzn.
- Jeśli wybrana jest reguła 'required’:true w formularzu to hook sprawdza czy ma do czynienia z tą metodą, oraz czy wartość wprowadzonego tekstu do pola formularza jest równa 0. W takim przypadku obiekt errors jest uzupełniany o kolejny o kolejny element o kluczu będącym nazwą pola formularza.
// is required validation if (rules.required === true && event.target.value.length === 0) { setErrors(errors => ({...errors, [event.target.name]: 'This field is required'})) }
- Jeśli wybrana regułą to 'isEmail’:true w formularzu, to hook sprawdza czy wprowadzony tekst jest zgodny ze wzorcem tzn.
// is valid email address validation else if (rules.isEmail === true && !pattern.test(event.target.value)) { setErrors(errors => ({...errors, [event.target.name]: 'This email address is invalid'})) }
- Jeśli wybrana reguła to 'min’: liczba, to hook sprawdza czy wpisany tekst w polu formularza jest co najmniej o długości podanej jako wartość liczba np. 'min’: 6 zaakceptuje tekst o długości co najmniej 6 znaków. W przeciwnym przypadku nastąpi ustawienie odpowiedniego błędu. Do tekstu komunikatu błędu pobrana jest nazwa pola formularza tzn. tekst będzie się zmieniał w zależności jak będzie się nazywać zmienna.
// min value length validation else if (rules.min && event.target.value.length < rules.min) { setErrors(errors => ({...errors, [event.target.name]: [event.target.name.charAt(0).toUpperCase()] + [event.target.name.slice(1)] + ' is too short'})) }
- Jeśli wybrana reguła to match, to jako wartość dla klucza match podajemy nazwę pola formularza, z którym mają być porównywane wartości. Hook nie ma 'na stałe’ zapisanej nazwy porównywanego pola tzn.
// match validation else if (rules.match && event.target.value!==values[rules.match]) { setErrors(errors => ({...errors, [event.target.name]: "Passwords don't match"})) }
Jeśli żadne z powyższych warunków nie zostało spełnione, to wywoływany jest blok else:
else { delete errors[event.target.name] }