From bab9e0c34a326d2ecdce8bb1da64acf0251e35e7 Mon Sep 17 00:00:00 2001 From: Ivan Lapa Date: Wed, 1 Apr 2026 11:31:48 +0200 Subject: [PATCH] refactor: migrate forms to React Hook Form Replace manual useState-per-field form management with react-hook-form across Login, CommentForm, NewsletterForm, and AddBlog. Update Input and Textarea components to forward ref for RHF register compatibility. Add co-located tests for all migrated forms. Co-Authored-By: Claude Opus 4.6 (1M context) --- client/package-lock.json | 17 ++ client/package.json | 1 + .../src/components/admin/auth/Login/Login.jsx | 29 ++-- .../admin/auth/Login/Login.test.jsx | 106 +++++------- .../comment/CommentForm/CommentForm.jsx | 35 ++-- .../comment/CommentForm/CommentForm.test.jsx | 84 ++++++--- .../forms/NewsletterForm/NewsletterForm.jsx | 41 ++--- .../NewsletterForm/NewsletterForm.test.jsx | 71 ++++++++ client/src/components/ui/Input/Input.jsx | 6 +- .../src/components/ui/Textarea/Textarea.jsx | 6 +- client/src/pages/admin/AddBlog/AddBlog.jsx | 159 ++++++++---------- .../src/pages/admin/AddBlog/AddBlog.test.jsx | 83 +++++++++ client/src/test/setup.js | 2 +- client/vite.config.js | 6 +- 14 files changed, 411 insertions(+), 235 deletions(-) create mode 100644 client/src/components/forms/NewsletterForm/NewsletterForm.test.jsx create mode 100644 client/src/pages/admin/AddBlog/AddBlog.test.jsx diff --git a/client/package-lock.json b/client/package-lock.json index e4e98640..c9256f73 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,6 +16,7 @@ "quill": "^2.0.3", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-hook-form": "^7.72.0", "react-hot-toast": "^2.5.2", "react-router-dom": "^7.6.0", "tailwindcss": "^4.1.7" @@ -4321,6 +4322,22 @@ "react": "^19.1.0" } }, + "node_modules/react-hook-form": { + "version": "7.72.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.0.tgz", + "integrity": "sha512-V4v6jubaf6JAurEaVnT9aUPKFbNtDgohj5CIgVGyPHvT9wRx5OZHVjz31GsxnPNI278XMu+ruFz+wGOscHaLKw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-hot-toast": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", diff --git a/client/package.json b/client/package.json index 2191c7d9..ae8b7cc2 100644 --- a/client/package.json +++ b/client/package.json @@ -21,6 +21,7 @@ "quill": "^2.0.3", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-hook-form": "^7.72.0", "react-hot-toast": "^2.5.2", "react-router-dom": "^7.6.0", "tailwindcss": "^4.1.7" diff --git a/client/src/components/admin/auth/Login/Login.jsx b/client/src/components/admin/auth/Login/Login.jsx index 821610f7..9d019b4c 100644 --- a/client/src/components/admin/auth/Login/Login.jsx +++ b/client/src/components/admin/auth/Login/Login.jsx @@ -1,4 +1,5 @@ import React, { useState } from 'react' +import { useForm } from 'react-hook-form' import { useAppContext } from '../../../../context/AppContext' import { adminApi } from '../../../../api' import { Button, Input } from '../../../ui' @@ -7,16 +8,14 @@ import { MESSAGES } from '../../../../constants/messages' function Login() { const { setToken } = useAppContext() - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') const [loading, setLoading] = useState(false) + const { register, handleSubmit } = useForm() - const handleSubmit = async (e) => { - e.preventDefault() + const onSubmit = async (data) => { setLoading(true) - + try { - const response = await adminApi.login({ email, password }) + const response = await adminApi.login(data) if (response.data.success) { setToken(response.data.token) @@ -42,30 +41,28 @@ function Login() {

Enter your credentials to access the admin panel

- -
+ + setEmail(e.target.value)} placeholder='your email id' required className='border-b-2 border-gray-300 mb-6 rounded-none border-x-0 border-t-0' + {...register('email', { required: true })} /> - + setPassword(e.target.value)} placeholder='your password' required className='border-b-2 border-gray-300 mb-6 rounded-none border-x-0 border-t-0' + {...register('password', { required: true })} /> - -