iTranslated by AI
Common Pitfalls When Building Forms with React, TypeScript, and Material-UI
Introduction
This article summarizes the difficulties I encountered with Typescript types and behavior when creating forms using React. I'm using Material-Ui for styling.
File Structure
├── src/
├── components/
└── Form/
└──Form.tsx
└── index.tsx
└──App.tsx
import { createMuiTheme, makeStyles, ThemeProvider } from '@material-ui/core';
import { green, purple } from '@material-ui/core/colors';
import React from 'react';
import Form from '../components/Form';
const theme = createMuiTheme({
palette: {
primary: {
main: purple[500],
},
secondary: {
main: green[500],
},
background: {
default: '#f4f5fd',
},
},
overrides: {
MuiAppBar: {
root: {
transform: 'translateZ(0)',
},
},
},
props: {
MuiIconButton: {
disableRipple: true,
},
},
});
const useStyles = makeStyles({
appMain: {
paddingLeft: '320px',
width: '100%',
},
});
function App() {
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<div className={classes.appMain}>
<Form />
</div>
</ThemeProvider>
);
}
export default App;
Text Not Appearing in Input Tag
The problematic code was found in the Form.tsx file.
import React from 'react';
import { Grid, makeStyles, Paper, TextField } from '@material-ui/core';
const useStyles = makeStyles((theme) => ({
root: {
'& .MuiFormControl-root': {
width: '80%',
margin: theme.spacing(1),
},
},
pageContent: {
margin: theme.spacing(5),
padding: theme.spacing(3),
},
}));
export interface FormValue {
id: number;
fullName: string;
email: string;
}
const formValue: FormValue = {
id: 0,
fullName: '',
email: '',
};
const Form = () => {
const classes = useStyles();
return (
<Paper className={classes.pageContent}>
<form className={classes.root} autoComplete="off">
<Grid container>
<Grid item xs={6}>
<TextField
variant="outlined"
label="お名前"
name="fullName"
value={formValue.fullName}
/>
<TextField
variant="outlined"
label="メールアドレス"
name="email"
value={formValue.email}
/>
</Grid>
</Grid>
</form>
</Paper>
);
};
export default Form;
Thanks to Material-Ui, the effects are quite cool.
However, the problem was that text was not reflected in the text field.
This was because the value attribute, a property of the text field, was set to an empty string. (It took me several tens of minutes to figure out why such a simple thing wasn't working.) As a solution, I assigned a function (handleInputChange) to the onChange attribute and used useState within that function.
Here's a partial display of the improved Form.tsx code.
const Form = () => {
const classes = useStyles();
const [values, setValues] = useState(formValue);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setValues({
...values,
[name]: value, // Shorthand for fullName:value, email:value
});
};
return (
<Paper className={classes.pageContent}>
<form className={classes.root} autoComplete="off">
<Grid container>
<Grid item xs={6}>
<TextField
variant="outlined"
label="お名前"
name="fullName"
value={values.fullName}// Changed from formValue to values
onChange={handleInputChange}
/>
<TextField
variant="outlined"
label="メールアドレス"
name="email"
value={values.email}// Changed from formValue to values
onChange={handleInputChange}
/>
</Grid>
</Grid>
</form>
</Paper>
);
};
Let me briefly explain the code.
Within handleInputChange, event.target contains variant, label, name, and value attributes, but I've destructured it to extract only the necessary name and value attributes.
Also, while the event object is taken as an argument, I was unsure what type to specify. I referred to a Qiita article (Don't give up on the any type React.EventCallback) for guidance.
Thanks to these code improvements, text is now successfully reflected in the text field!
Discussion