reactjs - 如何验证material ui stepper中的每个表单步骤?
问题描述
下面是典型的 material-ui 步进器。
export default function HorizontalLinearStepper() {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [skipped, setSkipped] = React.useState(new Set());
const steps = getSteps();
const isStepOptional = step => {
return step === 1;
};
const isStepSkipped = step => {
return skipped.has(step);
};
const handleNext = () => {
let newSkipped = skipped;
if (isStepSkipped(activeStep)) {
newSkipped = new Set(newSkipped.values());
newSkipped.delete(activeStep);
}
setActiveStep(prevActiveStep => prevActiveStep + 1);
setSkipped(newSkipped);
};
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
};
const handleSkip = () => {
if (!isStepOptional(activeStep)) {
// You probably want to guard against something like this,
// it should never occur unless someone's actively trying to break something.
throw new Error("You can't skip a step that isn't optional.");
}
setActiveStep(prevActiveStep => prevActiveStep + 1);
setSkipped(prevSkipped => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
};
const handleReset = () => {
setActiveStep(0);
};
return (
<div className={classes.root}>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
const labelProps = {};
if (isStepOptional(index)) {
labelProps.optional = <Typography variant="caption">Optional</Typography>;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} {...stepProps}>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
<div>
{activeStep === steps.length ? (
<div>
<Typography className={classes.instructions}>
All steps completed - you're finished
</Typography>
<Button onClick={handleReset} className={classes.button}>
Reset
</Button>
</div>
) : (
<div>
<div className={classes.instructions}>{getStepContent(activeStep)}</div>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
{isStepOptional(activeStep) && (
<Button
variant="contained"
color="primary"
onClick={handleSkip}
className={classes.button}
>
Skip
</Button>
)}
<Button
variant="contained"
color="primary"
onClick={handleNext}
type="submit"
className={classes.button}
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
)}
</div>
</div>
);
}
这是我为选择步骤创建的功能
function getStepContent(step) {
switch (step) {
case 0:
return <Step1/>;
case 1:
return <Step2/>;
case 2:
return 'This is the bit I really care about!';
default:
return 'Unknown step';
}
}
Step1 和 Step2 是在内部构建有 2 个表单的组件,其中包含 react-final-form
import React, { Component } from 'react';
import { Form } from 'react-final-form';
import initialValuesCreator from './creationMethods/initialValuesCreator';
import { validationCreator } from './creationMethods/validationSchemaCreator';
class CustomValidationForm extends Component {
render() {
const {
config ,children, submit = () => {}
} = this.props;
return (
<Form
onSubmit={(event) => {
submit(event);
}}
initialValues={initialValuesCreator(config)}
validate={values => validationCreator(values, config)}
render={({handleSubmit}) => (
<form noValidate autoComplete={'off'} onSubmit={handleSubmit}>
{children}
</form>
)}
/>
)
}
}
问题来了。Material ui stepper 有handleNext 功能。它是我为每个步骤提交的。每个步骤都将是某种带有验证的表单。当用户在第 1 步并按下提交时,我想向他展示输入错误(需要某些内容等)并防止跳转到下一步。Step1 可以有多个小表单,因此在按下 handleNext 时应验证所有表单。
解决方案
我也在处理它我最终做了什么来解决它:
- 用表单(而不是 div)包装页面
- 使用“useRef”反应钩子设置对表单的引用
- 检查表单在“handleNext”方法上是否有效,如果不是则返回。对表单对象的访问是通过 useRef 钩子实现的,而有效性检查是通过函数 'myForm.current.checkValidity()' 来实现的
请试一试,让我知道它是否适合您:
export default function HorizontalLinearStepper() {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [skipped, setSkipped] = React.useState(new Set());
const steps = getSteps();
const myForm = React.useRef(null);
const isStepOptional = step => {
return step === 1;
};
const isStepSkipped = step => {
return skipped.has(step);
};
const handleNext = () => {
if (!myForm.current.checkValidity()) {
return;
}
let newSkipped = skipped;
if (isStepSkipped(activeStep)) {
newSkipped = new Set(newSkipped.values());
newSkipped.delete(activeStep);
}
setActiveStep(prevActiveStep => prevActiveStep + 1);
setSkipped(newSkipped);
};
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
};
const handleSkip = () => {
if (!isStepOptional(activeStep)) {
// You probably want to guard against something like this,
// it should never occur unless someone's actively trying to break something.
throw new Error("You can't skip a step that isn't optional.");
}
setActiveStep(prevActiveStep => prevActiveStep + 1);
setSkipped(prevSkipped => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
};
const handleReset = () => {
setActiveStep(0);
};
return (
<div className={classes.root}>
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
const labelProps = {};
if (isStepOptional(index)) {
labelProps.optional = <Typography variant="caption">Optional</Typography>;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} {...stepProps}>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
<form action="/" method="POST" ref={myForm}>
{activeStep === steps.length ? (
<div>
<Typography className={classes.instructions}>
All steps completed - you're finished
</Typography>
<Button onClick={handleReset} className={classes.button}>
Reset
</Button>
</div>
) : (
<div>
<div className={classes.instructions}>{getStepContent(activeStep)}</div>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
{isStepOptional(activeStep) && (
<Button
variant="contained"
color="primary"
onClick={handleSkip}
className={classes.button}
>
Skip
</Button>
)}
<Button
variant="contained"
color="primary"
onClick={handleNext}
type="submit"
className={classes.button}
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</form>
)}
</div>
</div>
);
}
推荐阅读
- audacity - 大胆:静音音频仍然播放一些噪音
- python - 将 json 文档索引到 Elasticsearch 会引发 TransportError
- python - 使用python拆分csv文件中的单列
- python - 列表列表的乘法
- ruby-on-rails - Rails 应用程序意外安装在根文件夹中
- c# - mscorlib.dll 中出现“System.FormatException”类型的未处理异常附加信息:输入字符串的格式不正确
- python - Keras Tokenizer 中词索引的长度
- binding - 使用 Aurelia Property Observer 忽略您自己的更改的最佳方法
- node.js - NPM 全局安装
- json - 使用 Swift JSON 从 TheMovieDB API 获取数据时遇到问题