gpt4 book ai didi

javascript - 表单验证和清除

转载 作者:行者123 更新时间:2023-12-02 22:40:43 25 4
gpt4 key购买 nike

我是一名刚接触 React 的前端开发人员。仍在尝试弄清楚基础知识。我正在编写一个应用程序,要求用户从弹出对话框中输入表单中的信息。当框关闭时,我希望表单完全重置/清除,如果数据有效,我希望将其添加到状态并显示在表格中。

我的表单数据输入部分已经正常工作,但我目前正在努力清除表单并实现验证。具体来说,当表单关闭(取消按钮)时,它需要清除并重置,直到对话框再次打开。当按下提交按钮时,我需要运行某种表单验证来检查每个值在添加到状态之前是否正确输入。我没有比我高的人来审查我的代码并提供提示/帮助,所以我希望有人能指出我正确的方向,并让我知道我在代码中做错了什么。

欢迎基于我的任何代码提供任何其他有用的提示。我知道这可能有点奇怪和困惑。我正在从示例中学习和构建,所以请原谅我糟糕/困惑的代码。

app.js 主要代码:

import React, { Component } from 'react';
import './App.css';
import Form from './Form'
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Button from '@material-ui/core/Button';
import Snackbar from '@material-ui/core/Snackbar';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/styles';

const styles = theme => ({
root: {
container: {
background: 'yellow'
},
paper: {
background: 'red'
},
div:{
background:'black'
},
table: {
width: '100%'
},
}
});

class App extends Component {
constructor() {
super();
this.state = {
dialogOpen: false,
formClear: true,
snackBarOpen: false,
snackBarMessage: 'default meesage',
tableData: {},
formData: {
projectName: '',
jobNumber: '',
accountField: {
selected: [],
options:[
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'}
]
},
projectDescription: '',
designOrDigital: {
options:[
{label: 'option A', value: 'optionA'},
{label: 'option B', value: 'optionB'}
],
value: {}
},
mediaDeliverables: [
{label: 'different', checked: false, quantity:'', details:''},
{label: 'types', checked: false, quantity:'', details:''},
{label: 'of', checked: false, quantity:'', details:''},
{label: 'media', checked: false, quantity:'', details:''},
{label: 'deliverables', checked: false, quantity:'', details:''},
],
timing: {
entries : [
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
]
},
projectMap: {
selected: null
}
}
}

}

// -----------------
// SETTERS
// -----------------

setAccountsSelected = (selected) => {
let currentState = this.getStateCopy();
currentState.formData.accountField.selected = selected;
this.setState(currentState);
}
setProjectName = (value) => {
let currentState = this.getStateCopy();
currentState.formData.projectName = value;
this.setState(currentState);
}
setJobNumber = (value) => {
let currentState = this.getStateCopy();
currentState.formData.jobNumber = value;
this.setState(currentState);
}
setProjectDescription = (value) => {
let currentState = this.getStateCopy();
currentState.formData.projectDescription = value;
this.setState(currentState);
}
setDesignOrDigital = (value) => {
let currentState = this.getStateCopy();
currentState.formData.designOrDigital.value = value;
this.setState(currentState);
}
setMediaDeliverable = (value, index) => {
let currentState = this.getStateCopy();
currentState.formData.mediaDeliverables[index] = value;
this.setState(currentState);
}
setTimingData = data => {
let currentState = this.getStateCopy();
currentState.formData.timing.entries = data;
this.setState(currentState);
}
setSelectedProjectMapId = data => {
let currentState = this.getStateCopy();
currentState.formData.projectMap.selected = data;
this.setState(currentState);
}
setFormClear = (value) => {
let currentState = this.getStateCopy();
currentState.formClear = value;
this.setState(currentState);
}


// -----------------
// HANDLERS
// -----------------

onDialogOpen = () => this.setState({dialogOpen:true});

onDialogClose = () => this.setState({dialogOpen:false});

onSnackbarClose = (e, reason) => {
if (reason === 'clickaway') {
return;
}

this.setState({snackBarOpen:false});
this.setState({snackBarMessage:''});
};

onCreate = () => {

let theSnackBarMessage = `${this.state.formData.projectName} created`;
this.setState({snackBarOpen:true});
this.setState({snackBarMessage:theSnackBarMessage});
this.onDialogClose();
};

// I was told to use Object.assign to avoid direct mutation of state..
getStateCopy = () => Object.assign({}, this.state);

render() {
const { classes } = this.props;
return (
<div className="App">
<br /><br /><br />
<Button color="primary" onClick={this.onDialogOpen}>
Create New Resourcing Request
</Button>
<br /><br /><br />
<div>Table of information will go here</div>
<Dialog
className={classes.root}
open={this.state.dialogOpen}
onClose={this.onDialogClose}

maxWidth = {'md'}
>
<DialogTitle>Resource Request Form</DialogTitle>
<DialogContent>
<Form
projectName={this.state.formData.projectName}
jobNumber={this.state.formData.jobNumber}
accountSelected={this.state.formData.accountField.selected}
projectDescription={this.state.formData.projectDescription}
selectedProjectMapId={this.state.formData.projectMap.selected}

accountFieldOptions={this.state.formData.accountField.options}
designOrDigital={this.state.formData.designOrDigital}
mediaDeliverablesOptions={this.state.formData.mediaDeliverables}

timingData={this.state.formData.timing.entries}

setAccountsSelected={this.setAccountsSelected}
setProjectName={this.setProjectName}
setDesignOrDigital={this.setDesignOrDigital}
setMediaDeliverable={this.setMediaDeliverable}
setJobNumber={this.setJobNumber}
setProjectDescription={this.setProjectDescription}
setTimingData={this.setTimingData}
setSelectedProjectMapId={this.setSelectedProjectMapId}
setFormClear={this.setFormClear}

dialogOpen = {this.state.dialogOpen}
formClear = {this.state.formClear}
/>
</DialogContent>
<DialogActions>
<Button onClick={this.onDialogClose} color="primary">
Cancel
</Button>
<Button
variant="contained"
onClick={this.onCreate}
color="primary"
>
Create
</Button>
</DialogActions>
</Dialog>
<Snackbar
open={this.state.snackBarOpen}
message={this.state.snackBarMessage}
onClose={this.onSnackbarClose}
autoHideDuration={4000}
/>
</div>
);

}
}

App.propTypes = {
classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(App);
//export default App;

Form.js 组件:

import React, { useState, useEffect } from 'react';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import Fab from '@material-ui/core/Fab';
import SendIcon from '@material-ui/icons/Send';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormHelperText from '@material-ui/core/FormHelperText';
import TextField from '@material-ui/core/TextField';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';
import Checkbox from '@material-ui/core/Checkbox';
import Radio from '@material-ui/core/Radio';
import MediaDeliverablesCheckBox from './MediaDeliverablesCheckBox';
import { default as MaterialRadioGroup } from '@material-ui/core/RadioGroup';
import TimingTable from './TimingTable';
import ProjectMap from './ProjectMap';

const useStyles = makeStyles(theme => ({
container: {
display: 'inline-block',
flexWrap: 'wrap',
width: 1000,
},
extendedIcon: {
marginRight: theme.spacing(1),
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: 370,
},
dense: {
marginTop: 19,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
table: {
width: '20%',
}
}));

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};

let accountNames = [];

function getStyles(name, accountName, theme) {
return {
fontWeight:
accountName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}

const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
<FormControl>
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<MediaDeliverablesCheckBox
key={index}
index={index}
mediaDeliverablesOptions={value}
onMediaDeliverableChange={onMediaDeliverableChange(index)}
/>
))}
</FormGroup>
</FormControl>
);

const CheckboxGroup = ({ values, label, onChange }) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
checked={value.checked}
onChange={onChange(index)}
/>
}
label={value.label}
/>
))}
</FormGroup>
</FormControl>
);

const RadioGroup = ({ value, options, name, label, onChange }) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}

</FormLabel>
<MaterialRadioGroup
name={name}
value={value}
onChange={onChange}
disabled
>
{options.map((option, index) => (
<FormControlLabel
key={index}
control={<Radio />}
value={option.value}
label={option.label}
/>
))}
</MaterialRadioGroup>
</FormControl>
);

export default function Form(props) {

const classes = useStyles();
const theme = useTheme();
const designOrDigital = props.designOrDigital;
const mediaDeliverablesOptions = props.mediaDeliverablesOptions;
const accountName = props.accountSelected;
const projectName = props.projectName;

accountNames = [...props.accountFieldOptions];

useEffect(() => {
return () => {
//I started experimenting with lifecycle hooks because I thought
//I might need to clear the form at a certain point in the form
//lifcycle, but I'm not 100% sure where I need to do this..
console.log('>> [Form.js] useEffect() = will unmount');
console.log('>> [Form.js] will unmount: checking if form is closed and clear: dialogOpen=',props.dialogOpen,'formClear=',props.formClear);
}
}, []);

useEffect(() => {
console.log('>> [Form.js] useEffect() = mounted or updated');
console.log('>> [Form.js] mounted/updated: checking if form is closed and clear: dialogOpen=',props.dialogOpen,'formClear=',props.formClear);
});

const handleAccountChange = (event) => {
props.setFormClear(false);
props.setAccountsSelected(event.target.value);
}
const handleProjectNameChange = (event) => {
props.setFormClear(false);
props.setProjectName(event.target.value);
};
const handleJobNumberChange = (event) => {
props.setFormClear(false);
props.setJobNumber(event.target.value);
};
const handleProjectDescriptionChange = (event) => {
props.setFormClear(false);
props.setProjectDescription(event.target.value);
};
const onDesignOrDigitalChange = index => ({ target: { value } }) => {
props.setFormClear(false);
props.setDesignOrDigital(value);
};
const onMediaDeliverableChange = index => (deliverableData, e) => {
props.setFormClear(false);
props.setMediaDeliverable(deliverableData, index);
};
const timingChangeHandler = data => {
props.setFormClear(false);
props.setTimingData(data);
};

const clearForm = () => {
console.log('>> form being cleared');
//I couldn't figure out how to actually clear the form
//I experimented with having a boolean value in state to
//flag that form should be cleared.
//I attempted to manually clear the form field values via
//state but it doesn't appear to be working.
props.setFormClear(true);
props.setAccountsSelected([]); //pass empty array to clear current array
props.setProjectName(''); //pass empty string to clear current field.
}


console.log('>> [Form.js] (Form) checking if form is closed and clear: ',props.dialogOpen, props.formClear)
if(props.dialogOpen === false && props.formClear === false) {
//I was trying to watch the form state here to tell teh Form component
//when it needs to clear.. it didn't work so I started experimenting with
//lifecycle hooks above..
console.log('>> [Form.js] (Form) Form is Closed');
clearForm();
}

return (
<div>
<TextField
id="project-name"
label="Project Name"
className={classes.textField}
value={props.projectName}
onChange={handleProjectNameChange}
/>
<br /><br />
<FormControl className={classes.formControl}>
<InputLabel htmlFor="select-multiple-accounts">Account</InputLabel>
<Select
multiple
value={accountName}
onChange={handleAccountChange}
input={<Input id="select-multiple-accounts" />}
renderValue={
selected => (
<div className={classes.chips}>
{
selected.map(value => (
<Chip key={value} label={value} className={classes.chip} />
))}
</div>
)}
MenuProps={MenuProps}
>
{accountNames.map(name => (
<MenuItem key={name.label} value={name.label} style={getStyles(name.label, accountName, theme)}>
{name.label}
</MenuItem>
))}
</Select>
</FormControl>
<br /><br />
<TextField
id="job-number"
label="Job #"
className={classes.textField}
value={props.jobNumber}
onChange={handleJobNumberChange}
fullWidth
/>
<br /><br /><br />
<TextField
id="project-description"
label="Project Description"
placeholder="Provide a detailed description of the project:"
className={classes.textField}
multiline
variant="outlined"
value={props.projectDescription}
onChange={handleProjectDescriptionChange}
fullWidth
/>
<br /><br /><br />
<RadioGroup
label="Please Choose One:"
options={designOrDigital.options}
value={designOrDigital.value.value}
onChange={onDesignOrDigitalChange}
name="designOrDigitalRadio"
/>
<br /><br /><br />
<MediaDeliverableCheckBoxList
label="Please choose deliverables:"
onMediaDeliverableChange={onMediaDeliverableChange}
values={mediaDeliverablesOptions}
/>
<br /><br /><br />
<TimingTable
timingData={props.timingData}
timingChangeHandler={timingChangeHandler}
/>
<ProjectMap
setSelectedProjectMapId={props.setSelectedProjectMapId}
selectedProjectMapId={props.selectedProjectMapId}
/>
</div>
);
}

最佳答案

据我了解,您正在寻找一种方法使您的代码更清晰可维护,在提交时实现验证和逻辑。

ReactJS 中的表单可能会有点乏味,这就是为什么像 Formik 这样的工具和redux & redux-form已介绍。

我个人使用 Formik 在 React 中创建表单,我认为它应该被视为一个重要的 ReactJS 包。 我强烈建议您使用它,除非您有合理的理由不使用它。

Formik 可以处理所有与表单相关的逻辑,而无需编写一行代码,而且 Formik 被配置为使用 Yup这是一个用于表单对象模式验证的 npm 包。使用这些工具会让让您的生活更轻松

关于javascript - 表单验证和清除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58596566/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com