From c14a2bc0820299af61b2d983e7d8d1cee4fe913c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E7=A7=8B=E6=97=AD?= Date: Thu, 2 Mar 2023 17:24:03 +0800 Subject: [PATCH] replace formik with react-hook-form --- package.json | 3 +- pnpm-lock.yaml | 56 ++++++++-------------- src/pages/components/DeleteUser.tsx | 56 +++++++++++----------- src/pages/forget-password.page.tsx | 73 +++++++++++++---------------- src/pages/login.page.tsx | 45 +++++++++--------- src/pages/register.page.tsx | 68 ++++++++++++--------------- 6 files changed, 137 insertions(+), 164 deletions(-) diff --git a/package.json b/package.json index 40feb01..e9d0c74 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,16 @@ "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "@fontsource/roboto": "^4.5.8", + "@hookform/resolvers": "^2.9.11", "@mui/icons-material": "^5.11.9", "@mui/material": "^5.11.9", "@next/font": "13.1.6", "axios": "^1.3.3", - "formik": "^2.2.9", "http-status": "^1.6.2", "next": "13.1.6", "react": "18.2.0", "react-dom": "18.2.0", + "react-hook-form": "^7.43.2", "react-toastify": "^9.1.1", "swr": "^2.0.3", "yup": "^1.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56147d9..9f50692 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,7 @@ specifiers: '@emotion/react': ^11.10.6 '@emotion/styled': ^11.10.6 '@fontsource/roboto': ^4.5.8 + '@hookform/resolvers': ^2.9.11 '@mui/icons-material': ^5.11.9 '@mui/material': ^5.11.9 '@next/font': 13.1.6 @@ -14,7 +15,6 @@ specifiers: eslint: ^8.34.0 eslint-config-next: ^13.1.6 eslint-config-prettier: ^8.6.0 - formik: ^2.2.9 http-status: ^1.6.2 husky: ^8.0.0 lint-staged: ^13.1.2 @@ -22,6 +22,7 @@ specifiers: prettier: 2.8.4 react: 18.2.0 react-dom: 18.2.0 + react-hook-form: ^7.43.2 react-toastify: ^9.1.1 swr: ^2.0.3 typescript: ^4.9.5 @@ -31,15 +32,16 @@ dependencies: '@emotion/react': 11.10.6_pmekkgnqduwlme35zpnqhenc34 '@emotion/styled': 11.10.6_oouaibmszuch5k64ms7uxp2aia '@fontsource/roboto': 4.5.8 + '@hookform/resolvers': 2.9.11_react-hook-form@7.43.2 '@mui/icons-material': 5.11.9_ofpk46txu7v2f5mzrtv4xsczka '@mui/material': 5.11.9_xqeqsl5kvjjtyxwyi3jhw3yuli '@next/font': 13.1.6 axios: 1.3.3 - formik: 2.2.9_react@18.2.0 http-status: 1.6.2 next: 13.1.6_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 + react-hook-form: 7.43.2_react@18.2.0 react-toastify: 9.1.1_biqbaboplfbrettd7655fr4n2y swr: 2.0.3_react@18.2.0 yup: 1.0.0 @@ -243,6 +245,14 @@ packages: resolution: {integrity: sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==} dev: false + /@hookform/resolvers/2.9.11_react-hook-form@7.43.2: + resolution: {integrity: sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==} + peerDependencies: + react-hook-form: ^7.0.0 + dependencies: + react-hook-form: 7.43.2_react@18.2.0 + dev: false + /@humanwhocodes/config-array/0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} @@ -1104,11 +1114,6 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge/2.2.1: - resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} - engines: {node: '>=0.10.0'} - dev: false - /define-lazy-prop/2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} @@ -1668,21 +1673,6 @@ packages: mime-types: 2.1.35 dev: false - /formik/2.2.9_react@18.2.0: - resolution: {integrity: sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==} - peerDependencies: - react: '>=16.8.0' - dependencies: - deepmerge: 2.2.1 - hoist-non-react-statics: 3.3.2 - lodash: 4.17.21 - lodash-es: 4.17.21 - react: 18.2.0 - react-fast-compare: 2.0.4 - tiny-warning: 1.0.3 - tslib: 1.14.1 - dev: false - /fs.realpath/1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -2233,18 +2223,10 @@ packages: p-locate: 5.0.0 dev: true - /lodash-es/4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - dev: false - /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /lodash/4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: false - /log-update/4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} @@ -2629,8 +2611,13 @@ packages: scheduler: 0.23.0 dev: false - /react-fast-compare/2.0.4: - resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==} + /react-hook-form/7.43.2_react@18.2.0: + resolution: {integrity: sha512-NvD3Oe2Y9hhqo2R4I4iJigDzSLpdMnzUpNMxlnzTbdiT7NT3BW0GxWCzEtwPudZMUPbZhNcSy1EcGAygyhDORg==} + engines: {node: '>=12.22.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + dependencies: + react: 18.2.0 dev: false /react-is/16.13.1: @@ -3012,10 +2999,6 @@ packages: globrex: 0.1.2 dev: true - /tiny-warning/1.0.3: - resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - dev: false - /to-fast-properties/2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -3043,6 +3026,7 @@ packages: /tslib/1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true /tslib/2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} diff --git a/src/pages/components/DeleteUser.tsx b/src/pages/components/DeleteUser.tsx index 686871f..c0699cf 100644 --- a/src/pages/components/DeleteUser.tsx +++ b/src/pages/components/DeleteUser.tsx @@ -1,6 +1,5 @@ import * as React from 'react' import { useUser } from '@/utils/useUser' -import { useFormik } from 'formik' import { useRouter } from 'next/router' import Button from '@mui/material/Button' import TextField from '@mui/material/TextField' @@ -16,6 +15,8 @@ import * as yup from '@/utils/validation' import * as api from '@/api' import { useCountdown, COUNTDOWN_SECONDS } from '@/utils/useCountdown' import { toast } from 'react-toastify' +import { useForm, SubmitHandler } from 'react-hook-form' +import { yupResolver } from '@hookform/resolvers/yup' export function DeleteUser() { const { user } = useUser() @@ -24,33 +25,33 @@ export function DeleteUser() { const [confirm, setConfrim] = React.useState(false) const { countdown, setCountdown } = useCountdown() - const formik = useFormik({ - initialValues: { + const hookForm = useForm({ + mode: 'onTouched', + defaultValues: { email: user!.email, - password: '', - verifyCode: '', - token: '', - }, - validationSchema: yup.object({ - verifyCode: yup.verifyCodeSchema, - password: yup.string().required('请输入密码'), - }), - onSubmit: async (values) => { - await api.user.deleteUser(values) - localStorage.removeItem('accessToken') - toast.success('用户删除成功') - router.push('/login') }, + resolver: yupResolver( + yup.object({ + verifyCode: yup.verifyCodeSchema, + password: yup.string().required('请输入密码'), + }), + ), }) + const onSubmit: SubmitHandler = async (values) => { + await api.user.deleteUser(values) + localStorage.removeItem('accessToken') + toast.success('用户删除成功') + router.push('/login') + } async function sendVerifyCode() { setCountdown(COUNTDOWN_SECONDS) try { const res = await api.email.sendEmailVerifyCode({ - email: formik.values.email, + email: hookForm.getValues('email'), scene: EmailVerifyCodeScene.deleteUser, }) - formik.setFieldValue('token', res.data.token) + hookForm.setValue('token', res.data.token) } catch (err) { setCountdown(0) } @@ -73,7 +74,7 @@ export function DeleteUser() { @@ -107,12 +107,10 @@ export function DeleteUser() { - diff --git a/src/pages/forget-password.page.tsx b/src/pages/forget-password.page.tsx index f91868b..096fba4 100644 --- a/src/pages/forget-password.page.tsx +++ b/src/pages/forget-password.page.tsx @@ -6,7 +6,6 @@ import Grid from '@mui/material/Unstable_Grid2' import KeyIcon from '@mui/icons-material/Key' import Typography from '@mui/material/Typography' import Container from '@mui/material/Container' -import { useFormik } from 'formik' import { useRouter } from 'next/router' import * as yup from '@/utils/validation' import * as api from '@/api' @@ -14,43 +13,42 @@ import { ForgetPasswordInputDto } from '@/api/user.interface' import { EmailVerifyCodeScene } from '@/api/email.interface' import { useCountdown, COUNTDOWN_SECONDS } from '@/utils/useCountdown' import { toast } from 'react-toastify' +import { useForm, SubmitHandler } from 'react-hook-form' +import { yupResolver } from '@hookform/resolvers/yup' export default function ForgetPassword() { const router = useRouter() const { countdown, setCountdown } = useCountdown() - const formik = useFormik({ - initialValues: { - email: '', - password: '', - verifyCode: '', - token: '', - userId: '', - }, - validationSchema: yup.object({ - email: yup.emailSchema, - password: yup.passwordSchema, - verifyCode: yup.verifyCodeSchema, - }), - onSubmit: async (values) => { - const res = await api.user.forgetPassword(values) - localStorage.setItem('accessToken', res.data) - toast.success('密码重置成功!', { - onClose: () => router.push('/login'), - }) - }, + const hookForm = useForm({ + mode: 'onTouched', + resolver: yupResolver( + yup.object({ + email: yup.emailSchema, + password: yup.passwordSchema, + verifyCode: yup.verifyCodeSchema, + }), + ), }) + const onSubmit: SubmitHandler = async (values) => { + const res = await api.user.forgetPassword(values) + localStorage.setItem('accessToken', res.data) + toast.success('密码重置成功!', { + onClose: () => router.push('/login'), + }) + } async function sendVerifyCode() { - await formik.validateField('email') + const isValid = await hookForm.trigger('email') + if (!isValid) return setCountdown(COUNTDOWN_SECONDS) try { const res = await api.email.sendEmailVerifyCode({ - email: formik.values.email, + email: hookForm.getValues('email'), scene: EmailVerifyCodeScene.forgetPassword, }) - formik.setFieldValue('token', res.data.token) - formik.setFieldValue('userId', res.data.userId) + hookForm.setValue('token', res.data.token) + hookForm.setValue('userId', res.data.userId || '') } catch (err) { setCountdown(0) } @@ -77,14 +75,14 @@ export default function ForgetPassword() { container component="form" spacing={[2, 1]} - onSubmit={formik.handleSubmit} + onSubmit={hookForm.handleSubmit(onSubmit)} > @@ -118,12 +113,10 @@ export default function ForgetPassword() { ({ + mode: 'onTouched', + defaultValues: { email: '', password: '', }, - validationSchema: yup.object({ - email: yup.emailSchema, - password: yup.string().required('请输入密码'), - }), - onSubmit: async (values) => { - const res = await api.user.login(values) - localStorage.setItem('accessToken', res.data) - router.push('/') - }, + resolver: yupResolver( + yup.object({ + email: yup.emailSchema, + password: yup.string().required('请输入密码'), + }), + ), }) + const onSubmit: SubmitHandler = async (values) => { + const res = await api.user.login(values) + localStorage.setItem('accessToken', res.data) + router.push('/') + } return ( 登录 - + ({ - initialValues: { - email: '', - password: '', - verifyCode: '', - token: '', - }, - validationSchema: yup.object({ - email: yup.emailSchema, - password: yup.passwordSchema, - verifyCode: yup.verifyCodeSchema, - }), - onSubmit: async (values) => { - const res = await api.user.register(values) - localStorage.setItem('accessToken', res.data) - router.push('/') - }, + const hookForm = useForm({ + mode: 'onTouched', + resolver: yupResolver( + yup.object({ + email: yup.emailSchema, + password: yup.passwordSchema, + verifyCode: yup.verifyCodeSchema, + }), + ), }) + const onSubmit: SubmitHandler = async (values) => { + const res = await api.user.register(values) + localStorage.setItem('accessToken', res.data) + router.push('/') + } async function sendVerifyCode() { - await formik.validateField('email') + const isValid = await hookForm.trigger('email') + if (!isValid) return setCountdown(COUNTDOWN_SECONDS) try { const res = await api.email.sendEmailVerifyCode({ - email: formik.values.email, + email: hookForm.getValues('email'), scene: EmailVerifyCodeScene.register, }) - formik.setFieldValue('token', res.data.token) + hookForm.setValue('token', res.data.token) } catch (err) { setCountdown(0) } @@ -72,14 +71,14 @@ export default function Register() { container component="form" spacing={[2, 1]} - onSubmit={formik.handleSubmit} + onSubmit={hookForm.handleSubmit(onSubmit)} > @@ -113,14 +109,12 @@ export default function Register() {