## Giới thiệu Khi xây dựng các ứng dụng phản hồi nhanh trên nhiều nền tảng, việc duy trì tính đồng bộ giữa React Web và React Native thường đi kèm với những thách thức. Một trong những vấn đề phổ biến nhất là xác thực biểu mẫu: bạn muốn người dùng nhập dữ liệu chính xác trên cả máy tính để bàn và thiết bị di động, đồng thời cung cấp phản hồi nhất quán mà không lặp lại mã. ## Những thách thức chính - **Bộ thư viện API khác nhau** – các thành phần nhập liệu trên web và thiết bị di động hoạt động theo những cách khác nhau. - **Quản lý trạng thái tập trung** – việc theo dõi các lỗi xác thực trên cả hai nền tảng có thể trở nên phức tạp. - **Tái sử dụng logic** – việc sao chép schema xác thực dẫn đến việc duy trì mã không nhất quán. ## Tổng quan về giải pháp Chúng ta sẽ tạo ra một hệ thống xác thực duy nhất sử dụng: - **Schema xác thực dựa trên Yup** – một đối tượng xác thực duy nhất cho cả hai nền tảng. - **Custom hook (`useFormValidation`)** – cung cấp các hàm `validateField`, `validateAll` và trạng thái `errors`. - **Context API (React) / Hook Context (React Native)** – phân phối schema và lỗi thông qua cây component. - **React Query** – đồng bộ hóa dữ liệu biểu mẫu đã xác thực với backend một cách bất đồng bộ. ## Các bước triển khai ### 1. Tạo schema xác thực ```js import * as yup from 'yup'; const baseSchema = yup.object().shape({ email: yup.string().email('Email không hợp lệ').required('Email là bắt buộc'), password: yup.string().min(8, 'Mật khẩu phải có ít nhất 8 ký tự').required('Mật khẩu là bắt buộc'), }); ``` ### 2. Xây dựng custom hook ```js import { useState, useCallback } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { validationSchema } from './schema'; export const useFormValidation = () => { const [errors, setErrors] = useState({}); const client = useQueryClient(); const validateField = useCallback(async (name, value) => { try { await validationSchema.validateAt(name, { [name]: value }); setErrors(prev => ({ ...prev, [name]: null })); return true; } catch (err) { setErrors(prev => ({ ...prev, [name]: err.message })); return false; } }, []); const validateAll = useCallback(async (data) => { try { await validationSchema.validate(data, { abortEarly: false }); setErrors({}); // Gửi dữ liệu đã xác thực lên server thông qua React Query await client.setQueryData(['formData'], data); return true; } catch (validationErrors) { const formatted = {}; validationErrors.inner.forEach(e => { formatted[e.path] = e.message; }); setErrors(formatted); return false; } }, [client]); return { errors, validateField, validateAll }; }; ``` ### 3. Cung cấp hook thông qua context ```js import React, { createContext, useContext } from 'react'; import { useFormValidation } from './useFormValidation'; const ValidationContext = createContext(null); export const ValidationProvider = ({ children }) => { const value = useFormValidation(); return <ValidationContext.Provider value={value}>{children}</ValidationContext.Provider>; }; export const useValidation = () => useContext(ValidationContext); ``` ### 4. Sử dụng trong một thành phần nhập liệu ```js import React from 'react'; import { useValidation } from './ValidationContext'; export const EmailInput = () => { const { errors, validateField } = useValidation(); const handleChange = async (e) => { await validateField('email', e.target.value); }; return ( <div> <input type="email" onChange={handleChange} placeholder="you@example.com" /> {errors.email && <span className="error">{errors.email}</span>} </div> ); }; ``` ## Lời khuyên thực tế - **Tận dụng tính năng `abortEarly: false`** để thu thập tất cả các lỗi xác thực thay vì dừng lại ở lỗi đầu tiên. - **Tích hợp React Query** giúp bạn dễ dàng xử lý lỗi bất đồng bộ và cập nhật UI mà không cần gọi lại. - **Tách schema thành các phần** (ví dụ: `loginSchema` và `registerSchema`) để tái sử dụng trên nhiều màn hình. - **Xử lý lỗi tập trung** thông qua context giúp bạn tránh việc truyền props nhiều lần. ## Kết luận Bằng cách tập trung logic xác thực vào một schema Yup duy nhất và bao bọc nó trong một custom hook, bạn có thể chia sẻ cùng một bộ quy tắc xác thực giữa React Web và React Native. Phương pháp này giúp mã nguồn sạch sẽ, có thể kiểm tra và duy trì trong khi vẫn cung cấp phản hồi người dùng nhanh chóng trên tất cả các nền tảng.