ReactJS 验证中的无效挂钩调用
Invalid Hook Calls In Validation in ReactJS
我在 ReactJS 中实现登录验证时遇到问题。我正在使用 Material-UI 以及 Formik 和 Yap。我已经实现了它但是它有一个错误,它说无效的挂钩调用。钩子只能在函数组件的主体内部调用。
请检查我下面的代码:
import React, { useState } 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 } 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 InputAdornment from '@material-ui/core/InputAdornment';
import LockIcon from '@material-ui/icons/Lock';
import { withFormik } from 'formik';
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) => {
const { className, ...rest } = props;
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
const referer = props.referer;
const dispatch = useDispatch();
const signIn = () => {
dispatch(login(values.username, values.password));
};
const { classes, values, touched, errors, isSubmitting, handleChange, handleBlur, handleSubmit } = props;
if (isLoggedIn) {
return <Redirect to={referer} />;
}
return (
<Card {...rest} 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}>
<Button
color="primary"
variant="contained"
onClick={signIn}
className={classes.loginButton}
disabled={isSubmitting}
>
Log In
</Button>
</CardActions>
</form>
</Card>
);
};
let yup = require('yup');
const Form = 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 }) => {
setTimeout(() => {
// submit to the server
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 1000);
},
})(LoginForm);
LoginForm.propTypes = {
className: PropTypes.string,
};
export default LoginForm(useStyles)(Form);
Create a higher-order React component class that passes props and form handlers (the "FormikBag") into your component derived from supplied options.
只需导出 Form
而不是 LoginForm
。还要在组件内部使用 classes
do const classes = useStyles();
而不是将其作为道具传递。
Working copy of your code is here
代码段
const useStyles = makeStyles(theme => ({
root: {},
cardHeader: {
// backgroundColor: theme.palette.primary.main,
backgroundColor: "blue",
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 => {
const { className, ...rest } = props;
const classes = useStyles();
const isLoggedIn = true;
const referer = props.referer;
const signIn = () => {
console.log("signin");
};
const {
// classes,
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;
if (isLoggedIn) {
// return <Redirect to={referer} />;
console.log("isLoggedIn", isLoggedIn);
}
return (
<Card {...rest} 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}>
<Button
type="submit" //<----------- see here
color="primary"
variant="contained"
onClick={signIn}
className={classes.loginButton}
disabled={isSubmitting}
>
Log In
</Button>
</CardActions>
</form>
</Card>
);
};
let yup = require("yup");
const Form = 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 }) => {
setTimeout(() => {
// submit to the server
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 1000);
}
})(LoginForm);
LoginForm.propTypes = {
className: PropTypes.string
};
// export default LoginForm(useStyles)(Form);
export default Form;
我在 ReactJS 中实现登录验证时遇到问题。我正在使用 Material-UI 以及 Formik 和 Yap。我已经实现了它但是它有一个错误,它说无效的挂钩调用。钩子只能在函数组件的主体内部调用。
请检查我下面的代码:
import React, { useState } 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 } 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 InputAdornment from '@material-ui/core/InputAdornment';
import LockIcon from '@material-ui/icons/Lock';
import { withFormik } from 'formik';
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) => {
const { className, ...rest } = props;
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
const referer = props.referer;
const dispatch = useDispatch();
const signIn = () => {
dispatch(login(values.username, values.password));
};
const { classes, values, touched, errors, isSubmitting, handleChange, handleBlur, handleSubmit } = props;
if (isLoggedIn) {
return <Redirect to={referer} />;
}
return (
<Card {...rest} 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}>
<Button
color="primary"
variant="contained"
onClick={signIn}
className={classes.loginButton}
disabled={isSubmitting}
>
Log In
</Button>
</CardActions>
</form>
</Card>
);
};
let yup = require('yup');
const Form = 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 }) => {
setTimeout(() => {
// submit to the server
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 1000);
},
})(LoginForm);
LoginForm.propTypes = {
className: PropTypes.string,
};
export default LoginForm(useStyles)(Form);
Create a higher-order React component class that passes props and form handlers (the "FormikBag") into your component derived from supplied options.
只需导出 Form
而不是 LoginForm
。还要在组件内部使用 classes
do const classes = useStyles();
而不是将其作为道具传递。
Working copy of your code is here
代码段
const useStyles = makeStyles(theme => ({
root: {},
cardHeader: {
// backgroundColor: theme.palette.primary.main,
backgroundColor: "blue",
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 => {
const { className, ...rest } = props;
const classes = useStyles();
const isLoggedIn = true;
const referer = props.referer;
const signIn = () => {
console.log("signin");
};
const {
// classes,
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;
if (isLoggedIn) {
// return <Redirect to={referer} />;
console.log("isLoggedIn", isLoggedIn);
}
return (
<Card {...rest} 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}>
<Button
type="submit" //<----------- see here
color="primary"
variant="contained"
onClick={signIn}
className={classes.loginButton}
disabled={isSubmitting}
>
Log In
</Button>
</CardActions>
</form>
</Card>
);
};
let yup = require("yup");
const Form = 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 }) => {
setTimeout(() => {
// submit to the server
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 1000);
}
})(LoginForm);
LoginForm.propTypes = {
className: PropTypes.string
};
// export default LoginForm(useStyles)(Form);
export default Form;