การสร้างไลบรารีการตรวจสอบ React ของคุณเอง: คุณลักษณะ (ตอนที่ 2)
เผยแพร่แล้ว: 2022-03-10การใช้ไลบรารีตรวจสอบความถูกต้องนั้นไม่ได้ยากขนาดนั้น ไม่ได้เพิ่มคุณสมบัติพิเศษทั้งหมดที่ทำให้ห้องสมุดตรวจสอบ ของคุณ ดีกว่าที่เหลือ
บทความนี้จะใช้ไลบรารีการตรวจสอบความถูกต้องต่อไปที่เราเริ่มนำไปใช้ในส่วนก่อนหน้าของชุดบทความนี้ นี่คือคุณสมบัติที่จะนำเราจากการพิสูจน์แนวคิดง่ายๆ ไปสู่ห้องสมุดที่ใช้งานได้จริง!
- ตอนที่ 1: พื้นฐาน
- ส่วนที่ 2: คุณสมบัติ
- ตอนที่ 3: ประสบการณ์
แสดงเฉพาะการตรวจสอบเมื่อส่ง
เนื่องจากเรากำลังตรวจสอบเหตุการณ์การเปลี่ยนแปลงทั้งหมด เราจึงแสดงข้อความแสดงข้อผิดพลาดของผู้ใช้เร็วเกินไปสำหรับประสบการณ์ที่ดีของผู้ใช้ มีสองสามวิธีที่เราสามารถบรรเทาปัญหานี้ได้
วิธีแก้ปัญหาแรกคือการจัดเตรียมแฟล็กที่ submitted
เป็นคุณสมบัติที่ส่งคืนของ useValidation
hook ด้วยวิธีนี้ เราสามารถตรวจสอบได้ว่าแบบฟอร์มถูกส่งหรือไม่ก่อนที่จะแสดงข้อความแสดงข้อผิดพลาด ข้อเสียคือ "แสดงรหัสข้อผิดพลาด" ของเรายาวขึ้นเล็กน้อย:
<label> Username <br /> <input {...getFieldProps('username')} /> {submitted && errors.username && ( <div className="error">{errors.username}</div> )} </label>
อีกวิธีหนึ่งคือการจัดเตรียมข้อผิดพลาดชุดที่สอง (เรียกมันว่า submittedErrors
) ซึ่งเป็นอ็อบเจกต์ว่างหาก submitted
เป็นเท็จ และอ็อบเจ็กต์ errors
หากเป็นจริง เราสามารถนำไปปฏิบัติได้ดังนี้
const useValidation = config => { // as before return { errors: state.errors, submittedErrors: state.submitted ? state.errors : {}, }; }
ด้วยวิธีนี้ เราสามารถทำลายประเภทของข้อผิดพลาดที่เราต้องการแสดงได้ง่ายๆ แน่นอน เราทำสิ่งนี้ได้ที่ไซต์การโทรเช่นกัน — แต่ด้วยการให้ข้อมูลที่นี่ เรากำลังดำเนินการเพียงครั้งเดียว แทนที่จะใช้ภายในผู้บริโภคทั้งหมด
- ดูการสาธิต CodeSandbox ที่แสดงวิธีการใช้ข้อผิดพลาดที่
submittedErrors
แสดงข้อความแสดงข้อผิดพลาด On-Blur
หลายคนต้องการให้แสดงข้อผิดพลาดเมื่อออกจากฟิลด์ใดฟิลด์หนึ่ง เราสามารถเพิ่มการสนับสนุนสำหรับสิ่งนี้ โดยการติดตามว่าฟิลด์ใดที่ "เบลอ" (นำทางออกจาก) และส่งคืนอ็อบเจกต์ blurredErrors
คล้ายกับข้อผิดพลาดที่ submittedErrors
ด้านบน
การใช้งานต้องการให้เราจัดการกับการกระทำประเภทใหม่ - blur
ซึ่งจะอัปเดตวัตถุสถานะใหม่ที่เรียกว่า blurred
:
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
เราจะสร้างคุณสมบัติใหม่ในออบเจ็กต์สถานะ blurred
โดยมีชื่อฟิลด์เป็นคีย์ ซึ่งบ่งชี้ ว่า ฟิลด์นั้นถูกเบลอ
ขั้นตอนต่อไปคือการเพิ่ม onBlur
prop ให้กับฟังก์ชัน getFieldProps
ซึ่งจะส่งการกระทำนี้เมื่อทำได้:
getFieldProps: fieldName => ({ // as before onBlur: () => { dispatch({ type: 'blur', payload: fieldName }); }, }),
สุดท้าย เราจำเป็นต้องจัดเตรียม blurredErrors
จากเบ็ด useValidation
เพื่อให้เราสามารถแสดงข้อผิดพลาดได้เมื่อจำเป็นเท่านั้น
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, };
ที่นี่ เราสร้างฟังก์ชันบันทึกที่คำนวณข้อผิดพลาดที่จะแสดงโดยพิจารณาว่าฟิลด์นั้นเบลอหรือไม่ เราคำนวณข้อผิดพลาดชุดนี้ใหม่ทุกครั้งที่ข้อผิดพลาดหรือวัตถุเบลอเปลี่ยนไป คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ useMemo
hook ได้ในเอกสารประกอบ
- ดูการสาธิต CodeSandbox
เวลาสำหรับ Refactor เล็กๆ
คอมโพเนนต์ useValidation
ของเรากำลังส่งคืนข้อผิดพลาดสามชุด ซึ่งส่วนใหญ่จะดูเหมือนเดิมในบางช่วงเวลา แทนที่จะไปตามเส้นทางนี้ เราจะให้ผู้ใช้ระบุในการกำหนดค่าเมื่อพวกเขาต้องการให้ข้อผิดพลาดในแบบฟอร์มปรากฏขึ้น
ตัวเลือกใหม่ของเรา — showErrors
— จะยอมรับ “ส่ง” (ค่าเริ่มต้น), “เสมอ” หรือ “เบลอ” เราสามารถเพิ่มตัวเลือกเพิ่มเติมได้ในภายหลัง ถ้าจำเป็น
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 }; };
เนื่องจากโค้ดจัดการข้อผิดพลาดเริ่มกินพื้นที่ส่วนใหญ่ของเรา เรากำลังจัดโครงสร้างใหม่ให้เป็นฟังก์ชันของตัวเอง ถ้าคุณไม่ปฏิบัติตาม Object.entries
และ .reduce
ไม่เป็นไร — เป็นการเขียนโค้ด for...in
ใหม่ในส่วนสุดท้าย

หากเราต้องการ onBlur หรือการตรวจสอบทันที เราสามารถระบุตัวแสดงข้อผิด useValidation
showError
เราได้
const config = { // as before showErrors: 'blur', }; const { getFormProps, getFieldProps, errors } = useValidation(config); // errors would now only include the ones that have been blurred
- ดูการสาธิต CodeSandbox
หมายเหตุเกี่ยวกับสมมติฐาน
“โปรดทราบว่าตอนนี้ฉันกำลังสมมติว่าแต่ละแบบฟอร์มต้องการแสดงข้อผิดพลาดในลักษณะเดียวกัน (เมื่อส่งเสมอ เบลอเสมอ ฯลฯ) นั่นอาจเป็นจริงสำหรับแอปพลิเคชันส่วนใหญ่ แต่อาจไม่ใช่สำหรับทุกคน การตระหนักถึงสมมติฐานของคุณเป็นส่วน สำคัญ ในการสร้าง API ของคุณ”
อนุญาตสำหรับการตรวจสอบข้าม
คุณลักษณะที่มีประสิทธิภาพจริงๆ ของไลบรารีการตรวจสอบความถูกต้องคือการอนุญาตให้มีการตรวจสอบข้าม นั่นคือ เพื่อกำหนดการตรวจสอบความถูกต้องของฟิลด์หนึ่งบนค่าของฟิลด์อื่น
ในการอนุญาตนี้ เราจำเป็นต้องทำให้ hook แบบกำหนดเองของเรายอมรับฟังก์ชันแทนที่จะเป็นอ็อบเจกต์ ฟังก์ชันนี้จะถูกเรียกใช้ด้วยค่าฟิลด์ปัจจุบัน การใช้งานจริงเป็นเพียงโค้ดสามบรรทัดเท่านั้น!
function useValidation(config) { const [state, dispatch] = useReducer(...); if (typeof config === 'function') { config = config(state.values); } }
ในการใช้คุณลักษณะนี้ เราสามารถส่งฟังก์ชันที่ส่งคืนอ็อบเจ็กต์การกำหนดค่าไปยัง useValidation
:
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' } } }));
ที่นี่ เราใช้ค่า fields.password
เพื่อให้แน่ใจว่าสองฟิลด์รหัสผ่านมีอินพุตเดียวกัน (ซึ่งเป็นประสบการณ์การใช้งานที่แย่มาก แต่สำหรับโพสต์บล็อกอื่น)
- ดูการสาธิต CodeSandbox ที่ไม่ยอมให้ชื่อผู้ใช้และรหัสผ่านเป็นค่าเดียวกัน
เพิ่มการช่วยสำหรับการเข้าถึงบางรายการ
สิ่งที่ควรทำเมื่อคุณรับผิดชอบอุปกรณ์ประกอบฉากของสนามคือการเพิ่มแท็กเพลงที่ถูกต้องตามค่าเริ่มต้น ซึ่งจะช่วยให้โปรแกรมอ่านหน้าจอสามารถอธิบายแบบฟอร์มของคุณได้
การปรับปรุงที่ง่ายมากคือการเพิ่ม aria-invalid="true"
หากฟิลด์มีข้อผิดพลาด มาปฏิบัติกัน:
const useValidation = config => { // as before return { // as before getFieldProps: fieldName => ({ // as before 'aria-invalid': String(!!errors[fieldName]), }), } };
นั่นเป็นโค้ดที่เพิ่มเข้ามา หนึ่ง บรรทัด และประสบการณ์ผู้ใช้ที่ดีขึ้น มาก สำหรับผู้ใช้โปรแกรมอ่านหน้าจอ
คุณอาจสงสัยว่าทำไมเราถึงเขียน String(!!state.errors[fieldName])
? state.errors[fieldName]
เป็นสตริง และตัวดำเนินการปฏิเสธสองครั้งจะให้บูลีนแก่เรา (และไม่ใช่แค่ค่าความจริงหรือเท็จ) อย่างไรก็ตาม คุณสมบัติ aria-invalid
ควรเป็นสตริง (นอกจากนี้ยังสามารถอ่าน "ไวยากรณ์" หรือ "การสะกดคำ" นอกเหนือจาก "จริง" หรือ "เท็จ") ดังนั้นเราจึงจำเป็นต้องบังคับบูลีนนั้นให้เทียบเท่ากับสตริง
ยังมีการปรับแต่งอีกสองสามอย่างที่เราสามารถทำได้เพื่อปรับปรุงการช่วยสำหรับการเข้าถึง แต่ดูเหมือนว่าจะเป็นการเริ่มต้นที่ยุติธรรม
ไวยากรณ์ข้อความตรวจสอบชวเลข
เครื่องมือตรวจสอบส่วนใหญ่ใน calidators
ตัววัด (และตัวตรวจสอบอื่น ๆ ส่วนใหญ่ฉันคิดว่า) ต้องการเพียงข้อความแสดงข้อผิดพลาด คงจะดีไม่น้อยถ้าเราสามารถส่งผ่านสตริงนั้นแทนอ็อบเจ็กต์ที่มีคุณสมบัติ message
ที่มีสตริงนั้นได้
มาปรับใช้ในฟังก์ชัน validateField
ของเรา:
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; }
ด้วยวิธีนี้ เราสามารถเขียนการกำหนดค่าการตรวจสอบของเราใหม่ได้ดังนี้:
const config = { username: { isRequired: 'The username is required', isEmail: 'The username should be a valid email address', }, };
สะอาดขึ้นมาก!
ค่าฟิลด์เริ่มต้น
บางครั้ง เราต้องตรวจสอบแบบฟอร์มที่กรอกไปแล้ว ตะขอแบบกำหนดเองของเรายังไม่รองรับ — มาเริ่มกันเลย!
ค่าฟิลด์เริ่มต้นจะถูกระบุในการกำหนดค่าสำหรับแต่ละฟิลด์ ในคุณสมบัติ initialValue
หากไม่ได้ระบุไว้ ค่าเริ่มต้นจะเป็นสตริงว่าง
เราจะสร้างฟังก์ชัน getInitialState
ซึ่งจะสร้างสถานะเริ่มต้นของตัวลดขนาดให้เรา
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, }; }
เราผ่านทุกฟิลด์ ตรวจสอบว่ามีคุณสมบัติ initialValue
หรือไม่ และตั้งค่าเริ่มต้นตามนั้น จากนั้นเราจะเรียกใช้ค่าเริ่มต้นเหล่านั้นผ่านเครื่องมือตรวจสอบและคำนวณข้อผิดพลาดเริ่มต้นด้วย เราคืนค่าออบเจ็กต์สถานะเริ่มต้น ซึ่งสามารถส่งต่อไปยังฮุ useReducer
ของเราได้
เนื่องจากเรากำลังแนะนำอุปกรณ์ประกอบฉากที่ไม่ตรวจสอบความถูกต้องในการกำหนดค่าฟิลด์ เราจึงต้องข้ามไปเมื่อเราตรวจสอบฟิลด์ของเรา ในการทำเช่นนั้น เราเปลี่ยนฟังก์ชัน validateField
ของเรา:
function validateField(fieldValue = '', fieldConfig) { const specialProps = ['initialValue']; for (let validatorName in fieldConfig) { if (specialProps.includes(validatorName)) { continue; } // as before } }
ขณะที่เราเพิ่มคุณสมบัติเช่นนี้ต่อไป เราก็สามารถเพิ่มคุณสมบัติเหล่านี้ในอาร์เรย์ specialProps
ของเราได้
- ดูการสาธิต CodeSandbox
สรุป
เรากำลังดำเนินการสร้างไลบรารีตรวจสอบความถูกต้องที่น่าทึ่ง เราได้เพิ่มคุณสมบัติมากมาย และตอนนี้เราค่อนข้างเป็นผู้นำที่มีความคิดดี
ในส่วนถัดไปของชุดนี้ เราจะเพิ่มคุณสมบัติพิเศษทั้งหมดที่ทำให้ห้องสมุดการตรวจสอบของเรามีแนวโน้มใน LinkedIn