如何在Next.js13+Chakra UI中用react-hook-form跳转至确认页
解决方案
针对你用Next.js13、Chakra UI和react-hook-form开发的跨页面表单需求,以下是几种无需查询参数的实现方案:
方案1:React Context + 客户端跳转(适合无刷新客户端场景)
通过React Context临时存储表单数据,客户端跳转后读取,适合不需要持久化数据的场景。
1. 创建表单数据Context
// app/context/FormDataContext.tsx 'use client'; import { createContext, useContext, useState, ReactNode } from 'react'; type FormDataContextType = { formData: { email?: string }; setFormData: (data: { email?: string }) => void; }; const FormDataContext = createContext<FormDataContextType | undefined>(undefined); export function FormDataProvider({ children }: { children: ReactNode }) { const [formData, setFormData] = useState<{ email?: string }>({}); return ( <FormDataContext.Provider value={{ formData, setFormData }}> {children} </FormDataContext.Provider> ); } export function useFormData() { const context = useContext(FormDataContext); if (!context) { throw new Error('useFormData必须在FormDataProvider内使用'); } return context; }
2. 在根布局中注入Provider
// app/layout.tsx import { FormDataProvider } from './context/FormDataContext'; export default function RootLayout({ children }: { children: ReactNode }) { return ( <html lang="zh-CN"> <body> <FormDataProvider>{children}</FormDataProvider> </body> </html> ); }
3. 修改NewForm组件提交逻辑
'use client'; import { useForm } from 'react-hook-form'; import { Box, FormControl, Input, Button, Text } from '@chakra-ui/react'; import { useRouter } from 'next/navigation'; import { useFormData } from '../context/FormDataContext'; type NewFormType = { email: string }; export const NewForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<NewFormType>(); const router = useRouter(); const { setFormData } = useFormData(); const onSubmit = handleSubmit((data) => { setFormData(data); router.push('/xxx/confirm'); }); return ( <Box p={4}> <form onSubmit={onSubmit}> <FormControl isInvalid={!!errors.email} mb={4}> <Input id="email" type="email" placeholder="请输入邮箱" {...register("email", { required: "邮箱不能为空" })} /> {errors.email && <Text color="red.500" mt={1}>{errors.email.message}</Text>} </FormControl> <Button type="submit" colorScheme="blue">Confirm</Button> </form> </Box> ); };
4. 确认页面读取数据
'use client'; import { Box, Heading, Text } from '@chakra-ui/react'; import { useFormData } from '../context/FormDataContext'; export default function ConfirmPage() { const { formData } = useFormData(); if (!formData.email) { return <Box p={4}>请先填写表单</Box>; } return ( <Box p={4}> <Heading size="md" mb={4}>确认信息</Heading> <Text>您输入的邮箱是:{formData.email}</Text> </Box> ); }
注意:页面刷新后Context数据会丢失,若需持久化可选用下面的方案。
方案2:Cookie存储临时数据(支持页面刷新)
将表单数据存入Cookie,跳转后读取,适合需要临时持久化数据的场景。
1. 修改NewForm组件提交逻辑
'use client'; import { useForm } from 'react-hook-form'; import { Box, FormControl, Input, Button, Text } from '@chakra-ui/react'; import { useRouter } from 'next/navigation'; type NewFormType = { email: string }; export const NewForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<NewFormType>(); const router = useRouter(); // 设置Cookie const setCookie = (name: string, value: string, days: number = 1) => { const expires = new Date(); expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000); document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/`; }; const onSubmit = handleSubmit((data) => { setCookie('tempEmail', data.email); router.push('/xxx/confirm'); }); return ( <Box p={4}> <form onSubmit={onSubmit}> <FormControl isInvalid={!!errors.email} mb={4}> <Input id="email" type="email" placeholder="请输入邮箱" {...register("email", { required: "邮箱不能为空" })} /> {errors.email && <Text color="red.500" mt={1}>{errors.email.message}</Text>} </FormControl> <Button type="submit" colorScheme="blue">Confirm</Button> </form> </Box> ); };
2. 确认页面读取Cookie
'use client'; import { Box, Heading, Text } from '@chakra-ui/react'; import { useEffect, useState } from 'react'; export default function ConfirmPage() { const [email, setEmail] = useState<string | null>(null); // 获取Cookie const getCookie = (name: string) => { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return decodeURIComponent(parts.pop()?.split(';').shift() || ''); return null; }; useEffect(() => { const tempEmail = getCookie('tempEmail'); setEmail(tempEmail); // 读取后清除Cookie,避免残留 document.cookie = 'tempEmail=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;'; }, []); if (!email) { return <Box p={4}>请先填写表单</Box>; } return ( <Box p={4}> <Heading size="md" mb={4}>确认信息</Heading> <Text>您输入的邮箱是:{email}</Text> </Box> ); }
方案3:Server Actions + 服务器会话(App Router推荐,安全持久)
通过Next.js Server Actions提交表单,将数据存入服务器会话(如iron-session),适合需要安全存储数据的场景。
1. 安装依赖
npm install iron-session
2. 配置会话
// app/lib/session.ts import { IronSessionOptions } from 'iron-session'; export const sessionOptions: IronSessionOptions = { password: process.env.SESSION_PASSWORD as string, // 需在.env中设置至少32位的密钥 cookieName: 'form-session', cookieOptions: { secure: process.env.NODE_ENV === 'production', }, }; declare module 'iron-session' { interface IronSessionData { tempEmail?: string; } }
3. 创建Server Action
// app/actions/formActions.ts 'use server'; import { cookies } from 'next/headers'; import { sessionOptions } from '../lib/session'; export async function submitForm(data: { email: string }) { const session = await cookies().getIronSession(sessionOptions); session.tempEmail = data.email; await session.save(); }
4. 修改NewForm组件调用Action
'use client'; import { useForm } from 'react-hook-form'; import { Box, FormControl, Input, Button, Text } from '@chakra-ui/react'; import { useRouter } from 'next/navigation'; import { submitForm } from '../actions/formActions'; type NewFormType = { email: string }; export const NewForm = () => { const { register, handleSubmit, formState: { errors }, } = useForm<NewFormType>(); const router = useRouter(); const onSubmit = handleSubmit(async (data) => { await submitForm(data); router.push('/xxx/confirm'); }); return ( <Box p={4}> <form onSubmit={onSubmit}> <FormControl isInvalid={!!errors.email} mb={4}> <Input id="email" type="email" placeholder="请输入邮箱" {...register("email", { required: "邮箱不能为空" })} /> {errors.email && <Text color="red.500" mt={1}>{errors.email.message}</Text>} </FormControl> <Button type="submit" colorScheme="blue">Confirm</Button> </form> </Box> ); };
5. 确认页面读取会话数据
// app/xxx/confirm/page.tsx import { Box, Heading, Text } from '@chakra-ui/react'; import { cookies } from 'next/headers'; import { sessionOptions } from '../../lib/session'; export default async function ConfirmPage() { const session = await cookies().getIronSession(sessionOptions); const email = session.tempEmail; // 读取后清除会话数据 session.tempEmail = undefined; await session.save(); if (!email) { return <Box p={4}>请先填写表单</Box>; } return ( <Box p={4}> <Heading size="md" mb={4}>确认信息</Heading> <Text>您输入的邮箱是:{email}</Text> </Box> ); }
补充说明
原代码中Confirm按钮不在<form>标签内,无法触发表单提交,以上方案已将按钮移入表单并设置type="submit",确保点击时自动触发验证和提交逻辑。
内容的提问来源于stack exchange,提问作者amu03
相关产品推荐
相关产品推荐

