Kendi React Validation Kitaplığınızı Oluşturma: Özellikler (Bölüm 2)
Yayınlanan: 2022-03-10Bir doğrulama kitaplığı uygulamak o kadar da zor değil. Doğrulama kitaplığınızı diğerlerinden çok daha iyi yapan tüm bu ekstra özellikleri de eklemiyor.
Bu makale, bu makale dizisinin önceki bölümünde uygulamaya başladığımız doğrulama kitaplığını uygulamaya devam edecek. Bunlar bizi basit bir kavram kanıtından gerçek bir kullanılabilir kütüphaneye götürecek olan özelliklerdir!
- Bölüm 1: Temel Bilgiler
- Bölüm 2: Özellikler
- Bölüm 3: Deneyim
Yalnızca Gönderildiğinde Doğrulamayı Göster
Tüm değişiklik olaylarını doğruladığımızdan, kullanıcı hata mesajlarını iyi bir kullanıcı deneyimi için çok erken gösteriyoruz. Bunu azaltmanın birkaç yolu var.
İlk çözüm, submitted
bayrağı useValidation
kancasının döndürülen bir özelliği olarak sağlamaktır. Bu şekilde, bir hata mesajı göstermeden önce formun gönderilip gönderilmediğini kontrol edebiliriz. Buradaki dezavantaj, "hata kodunu göster"imizin biraz daha uzun sürmesidir:
<label> Username <br /> <input {...getFieldProps('username')} /> {submitted && errors.username && ( <div className="error">{errors.username}</div> )} </label>
Başka bir yaklaşım, submitted
yanlışsa boş bir nesne ve doğruysa errors
nesnesi olan ikinci bir hata grubu (bunlara submittedErrors
diyelim) sağlamaktır. Bunu şu şekilde uygulayabiliriz:
const useValidation = config => { // as before return { errors: state.errors, submittedErrors: state.submitted ? state.errors : {}, }; }
Bu şekilde, göstermek istediğimiz hata türlerini basitçe yok edebiliriz. Bunu elbette çağrı sitesinde de yapabiliriz - ancak burada sağlayarak, tüm tüketiciler yerine bir kez uyguluyoruz.
-
submittedErrors
nasıl kullanılabileceğini gösteren CodeSandbox demosuna bakın.
Bulanıklaştırmada Hata Mesajlarını Göster
Pek çok insan, belirli bir alanı terk ettikten sonra bir hatanın gösterilmesini ister. Hangi alanların "bulanıklaştırıldığını" (uzaklaştırıldığını) izleyerek ve yukarıdaki blurredErrors
benzer şekilde submittedErrors
nesnesini döndürerek buna destek ekleyebiliriz.
Uygulama, blurred
adlı yeni bir durum nesnesini güncelleyecek olan yeni bir eylem türü olan blur
işlememizi gerektiriyor:
const initialState = { values: {}, errors: {}, blurred: {}, submitted: false, }; function validationReducer(state, action) { switch (action.type) { // as before case 'blur': const blurred = { ...state.blurred, [action.payload]: true }; return { ...state, blurred }; default: throw new Error('Unknown action type'); } }
blur
eylemini gönderdiğimizde, blurred
durum nesnesinde, alanın bulanıklaştırıldığını belirten , anahtar olarak alan adıyla yeni bir özellik oluştururuz.
Sonraki adım, uygun olduğunda bu eylemi gönderen getFieldProps
işlevimize bir onBlur
eklemektir:
getFieldProps: fieldName => ({ // as before onBlur: () => { dispatch({ type: 'blur', payload: fieldName }); }, }),
Son olarak, hataları yalnızca gerektiğinde gösterebilmemiz için blurredErrors
useValidation
blurredErrors sağlamamız gerekiyor.
const blurredErrors = useMemo(() => { const returnValue = {}; for (let fieldName in state.errors) { returnValue[fieldName] = state.blurred[fieldName] ? state.errors[fieldName] : null; } return returnValue; }, [state.errors, state.blurred]); return { // as before blurredErrors, };
Burada, alanın bulanık olup olmamasına bağlı olarak hangi hataların gösterileceğini belirleyen, not alınmış bir fonksiyon oluşturuyoruz. Hatalar veya bulanık nesneler değiştiğinde bu hata kümesini yeniden hesaplarız. Belgelerde useMemo
kancası hakkında daha fazla bilgi edinebilirsiniz.
- CodeSandbox demosuna bakın
Minik Bir Refactor Zamanı
useValidation
bileşenimiz şimdi, çoğu zaman içinde aynı görünecek olan üç grup hata döndürüyor. Bu yoldan gitmek yerine, kullanıcının formlarındaki hataların ne zaman görünmesini istediğini yapılandırmada belirtmesine izin vereceğiz.
Yeni seçeneğimiz - showErrors
- "gönder" (varsayılan), "her zaman" veya "bulanıklaştır" seçeneğini kabul edecektir. Gerekirse daha sonra daha fazla seçenek ekleyebiliriz.
function getErrors(state, config) { if (config.showErrors === 'always') { return state.errors; } if (config.showErrors === 'blur') { return Object.entries(state.blurred) .filter(([, blurred]) => blurred) .reduce((acc, [name]) => ({ ...acc, [name]: state.errors[name] }), {}); } return state.submitted ? state.errors : {}; } const useValidation = config => { // as before const errors = useMemo( () => getErrors(state, config), [state, config] ); return { errors, // as before }; };
Hata işleme kodu alanımızın çoğunu almaya başladığından, onu kendi işlevine göre yeniden yapılandırıyoruz. Object.entries
ve .reduce
öğelerini izlemezseniz - sorun değil - bu, son bölümdeki for...in
kodunun yeniden yazılmasıdır.
onBlur veya anında doğrulama istersek, useValidation
yapılandırma nesnemizde showError
belirtebiliriz.
const config = { // as before showErrors: 'blur', }; const { getFormProps, getFieldProps, errors } = useValidation(config); // errors would now only include the ones that have been blurred
- CodeSandbox demosuna bakın
Varsayımlar Üzerine Not
“Şimdi her formun hataları aynı şekilde göstermek isteyeceğini varsayıyorum (her zaman gönderildiğinde, her zaman bulanıklıkta vb.). Bu, çoğu uygulama için doğru olabilir, ancak muhtemelen hepsi için geçerli değildir. Varsayımlarınızın farkında olmak, API'nizi oluşturmanın büyük bir parçasıdır."
Çapraz Doğrulamaya İzin Ver
Doğrulama kitaplığının gerçekten güçlü bir özelliği, çapraz doğrulamaya izin vermesidir - yani, bir alanın doğrulamasını başka bir alanın değerine dayandırmaktır.
Buna izin vermek için, özel kancamızın bir nesne yerine bir işlevi kabul etmesini sağlamamız gerekir. Bu fonksiyon mevcut alan değerleri ile çağrılacaktır. Bunu uygulamak aslında sadece üç satırlık bir koddur!
function useValidation(config) { const [state, dispatch] = useReducer(...); if (typeof config === 'function') { config = config(state.values); } }
Bu özelliği kullanmak için, konfigürasyon nesnesini useValidation
döndüren bir işlevi basitçe iletebiliriz:
const { getFieldProps } = useValidation(fields => ({ password: { isRequired: { message: 'Please fill out the password' }, }, repeatPassword: { isRequired: { message: 'Please fill out the password one more time' }, isEqual: { value: fields.password, message: 'Your passwords don\'t match' } } }));
Burada, iki parola alanının aynı girdiyi içerdiğinden emin olmak için fields.password
değerini kullanırız (ki bu korkunç bir kullanıcı deneyimidir, ancak bu başka bir blog yazısı içindir).
- Kullanıcı adı ve parolanın aynı değerde olmasına izin vermeyen CodeSandbox demosuna bakın.
Bazı Erişilebilirlik Kazançları Ekleyin
Bir alanın desteklerinden sorumlu olduğunuzda yapılacak güzel bir şey, varsayılan olarak doğru aria etiketlerini eklemektir. Bu, ekran okuyucuların formunuzu açıklamasına yardımcı olacaktır.
Alanda bir hata varsa, çok basit bir iyileştirme aria-invalid="true"
eklemektir. Bunu uygulayalım:
const useValidation = config => { // as before return { // as before getFieldProps: fieldName => ({ // as before 'aria-invalid': String(!!errors[fieldName]), }), } };
Bu, eklenen bir kod satırı ve ekran okuyucu kullanıcıları için çok daha iyi bir kullanıcı deneyimidir.
Neden String(!!state.errors[fieldName])
merak ediyor olabilirsiniz? state.errors[fieldName]
bir dizedir ve çift olumsuzlama operatörü bize bir boole değeri verir (yalnızca doğru veya yanlış bir değer değil). Ancak, aria-invalid
özelliği bir dize olmalıdır ("doğru" veya "yanlış"a ek olarak "dilbilgisi" veya "yazım" da okuyabilir), bu nedenle bu boole değerini dize eşdeğerine zorlamamız gerekir.
Erişilebilirliği geliştirmek için yapabileceğimiz birkaç ince ayar daha var, ancak bu adil bir başlangıç gibi görünüyor.
Steno Doğrulama Mesajı Sözdizimi
calidators
paketindeki doğrulayıcıların çoğu (ve sanırım diğer doğrulayıcıların çoğu) yalnızca bir hata mesajı gerektirir. Bu dizgiyi içeren bir message
özelliğine sahip bir nesne yerine sadece bu dizgiyi iletebilsek güzel olmaz mıydı?
Bunu validateField
işlevimizde uygulayalım:
function validateField(fieldValue = '', fieldConfig, allFieldValues) { for (let validatorName in fieldConfig) { let validatorConfig = fieldConfig[validatorName]; if (typeof validatorConfig === 'string') { validatorConfig = { message: validatorConfig }; } const configuredValidator = validators[validatorName](validatorConfig); const errorMessage = configuredValidator(fieldValue); if (errorMessage) { return errorMessage; } } return null; }
Bu şekilde doğrulama yapılandırmamızı şu şekilde yeniden yazabiliriz:
const config = { username: { isRequired: 'The username is required', isEmail: 'The username should be a valid email address', }, };
Çok daha temiz!
İlk Alan Değerleri
Bazen, önceden doldurulmuş bir formu doğrulamamız gerekir. Özel kancamız henüz bunu desteklemiyor - hadi başlayalım!
İlk alan değerleri, her alan için yapılandırmada initialValue
özelliğinde belirtilecektir. Belirtilmezse, varsayılan olarak boş bir dize olur.
Bizim için redüktörümüzün ilk durumunu oluşturacak olan getInitialState
fonksiyonunu yaratacağız.
function getInitialState(config) { if (typeof config === 'function') { config = config({}); } const initialValues = {}; const initialBlurred = {}; for (let fieldName in config.fields) { initialValues[fieldName] = config.fields[fieldName].initialValue || ''; initialBlurred[fieldName] = false; } const initialErrors = validateFields(initialValues, config.fields); return { values: initialValues, errors: initialErrors, blurred: initialBlurred, submitted: false, }; }
Tüm alanları gözden geçiriyoruz, initialValue
özelliğine sahip olup olmadıklarını kontrol ediyoruz ve buna göre başlangıç değerini ayarlıyoruz. Ardından bu başlangıç değerlerini doğrulayıcılar aracılığıyla çalıştırır ve başlangıç hatalarını da hesaplarız. Daha sonra useReducer
iletilebilecek olan ilk durum nesnesini döndürürüz.
Alan yapılandırmasına doğrulayıcı olmayan bir destek eklediğimiz için, alanlarımızı doğrularken bunu atlamamız gerekiyor. Bunu yapmak için validateField
işlevimizi değiştiriyoruz:
function validateField(fieldValue = '', fieldConfig) { const specialProps = ['initialValue']; for (let validatorName in fieldConfig) { if (specialProps.includes(validatorName)) { continue; } // as before } }
Bunun gibi daha fazla özellik eklemeye devam ettikçe, bunları specialProps
ekleyebiliriz.
- CodeSandbox demosuna bakın
Özetliyor
Harika bir doğrulama kitaplığı oluşturma yolundayız. Tonlarca özellik ekledik ve şimdiye kadar neredeyse fikir liderleri olduk.
Bu serinin bir sonraki bölümünde, doğrulama kitaplığımızı LinkedIn'de trend haline getiren tüm ekstraları ekleyeceğiz.