ReactJS登录验证出现无效Hook调用问题求助
解决React中"无效Hook调用"的问题
首先,咱们来定位你代码里的核心问题:
错误根源
你最后一行的导出写法完全错误:
export default LoginForm(useStyles)(Form);
useStyles是一个React Hook,必须在函数组件的主体内部顶层调用,不能像普通工具函数那样传递给组件。而且你把withFormik包装后的Form组件反过来传给LoginForm,这完全搞反了组件的包装逻辑。
另外还有几个小问题:
signIn函数里直接用values会报错,因为values是Formik注入到组件的props,需要从props中解构获取- 按钮的
onClick绑定了signIn,但Formik已经提供了handleSubmit来处理表单提交,应该统一用Formik的提交流程
修正后的完整代码
import React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import { makeStyles } from '@material-ui/styles'; import { Card, CardHeader, CardContent, CardActions, Divider, Button, TextField, InputAdornment } from '@material-ui/core'; import { useSelector, useDispatch } from 'react-redux'; import { Redirect } from 'react-router-dom'; import { login } from '../../../actions'; import AccountCircle from '@material-ui/icons/AccountCircle'; import LockIcon from '@material-ui/icons/Lock'; import { withFormik } from 'formik'; import * as yup from 'yup'; // 改成import方式更规范 const useStyles = makeStyles((theme) => ({ root: {}, cardHeader: { backgroundColor: theme.palette.primary.main, color: 'white', display: 'flex', justifyContent: 'center', fontSize: '2rem', padding: '15px', }, textFieldSection: { padding: '40px 40px 0 40px', }, loginButtonSection: { padding: '18px 40px 40px 40px', }, loginButton: { width: '100%', height: '50px', textTransform: 'none', fontSize: '18px', }, })); const LoginForm = (props) => { // 在这里调用useStyles Hook,这是正确的位置 const classes = useStyles(); const { className, values, touched, errors, isSubmitting, handleChange, handleBlur, handleSubmit } = props; const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); const referer = props.referer; const dispatch = useDispatch(); if (isLoggedIn) { return <Redirect to={referer} />; } return ( <Card className={clsx(classes.root, className)}> <form onSubmit={handleSubmit}> <CardHeader title="LOGIN" classes={{ title: classes.cardHeader }} className={classes.cardHeader} /> <CardContent className={classes.textFieldSection}> <TextField fullWidth label="Username" name="username" type="text" variant="outlined" value={values.username} onChange={handleChange} onBlur={handleBlur} helperText={touched.username ? errors.username : ''} error={touched.username && Boolean(errors.username)} InputProps={{ endAdornment: ( <InputAdornment> <AccountCircle /> </InputAdornment> ), }} /> <TextField fullWidth label="Password" name="password" style={{ marginTop: '1rem' }} type="password" variant="outlined" value={values.password} onChange={handleChange} onBlur={handleBlur} helperText={touched.password ? errors.password : ''} error={touched.password && Boolean(errors.password)} InputProps={{ endAdornment: ( <InputAdornment> <LockIcon /> </InputAdornment> ), }} /> </CardContent> <CardActions className={classes.loginButtonSection}> {/* 修正:用form的onSubmit,这里按钮改成type="submit" */} <Button color="primary" variant="contained" type="submit" className={classes.loginButton} disabled={isSubmitting} > Log In </Button> </CardActions> </form> </Card> ); }; // 修正Formik的handleSubmit,在这里调用dispatch更符合流程 const FormikLoginForm = withFormik({ mapPropsToValues: ({ username, password }) => { return { username: username || '', password: password || '', }; }, validationSchema: yup.object().shape({ username: yup.string().required('Required'), password: yup.string().min(8, 'Password must contain at least 8 characters').required('Enter your password'), }), handleSubmit: (values, { setSubmitting, props }) => { const { dispatch } = props; dispatch(login(values.username, values.password)) .then(() => { setSubmitting(false); }) .catch(() => { setSubmitting(false); // 这里可以处理登录失败的逻辑 }); }, })(LoginForm); LoginForm.propTypes = { className: PropTypes.string, }; // 导出Formik包装后的组件 export default FormikLoginForm;
关键修改点说明
- Hook的正确调用:把
useStyles()移到LoginForm组件的顶层作用域内,这是React Hook的强制要求,确保Hook在每次组件渲染时都能按顺序执行。 - 组件导出逻辑修正:用
withFormik包装LoginForm得到FormikLoginForm,然后导出这个包装后的组件,彻底纠正之前错误的嵌套调用逻辑。 - 表单提交逻辑统一:把登录的dispatch逻辑移到Formik的
handleSubmit里,按钮改成type="submit",这样Formik会自动处理表单验证、提交状态管理等流程,避免手动绑定onClick的混乱。 - 规范导入:把
require('yup')改成import * as yup from 'yup',符合ES模块的规范写法。
这样修改后,"无效Hook调用"的错误应该就会消失,同时表单的逻辑也更清晰,符合Formik和React的最佳实践。
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

