フォームを作る過程でつまずいたこと

公開:2020/11/03
更新:2020/12/09
4 min読了の目安(約4100字TECH技術記事

はじめに

Reactを使ってフォームを作成するにあたってTypescriptによる型や動作などでつまずいたことを以下にまとめます。スタイルにはMaterial-Uiを用いてます。


ファイル構成

├── src/
   ├── components/
	   └── Form/
		└──Form.tsx
   └── index.tsx
   └──App.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;

imputタグに文字が表示されない

問題のコードはForm.tsxファイルにありました。

Form.tsx
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;

Material-Uiのおかげでエフェクトもかっこいいです。
React App

しかし問題となったのはテキストフィールド上に文字が反映されていないということです。

これはテキストフィールドのプロパティであるvalue属性に空文字が設定されているためです。(こんな単純なことになんで動かへんねん?と数十分かかりました。。。)改善策として、onChange属性に関数(handleInputChange)を割り当てて、その関数内でuseStateを使うという手段を取りました。

改善したForm.tsxのコードを一部表示します。

Form.tsxの一部
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,//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}//formValueからvaluesに変更
              onChange={handleInputChange}
            />
            <TextField
              variant="outlined"
              label="メールアドレス"
              name="email"
              value={values.email}//formValueからvaluesに変更
              onChange={handleInputChange}
            />
          </Grid>
        </Grid>
      </form>
    </Paper>
  );
};

少しコードを解説します。
handleInputChange内event.targetにはvariant,label,name,value属性が内包されていますが、必要なname属性とvalue属性を分割代入によって取り出しています。

またイベントオブジェクトを引数に取っていますが、どのような型を指定するべきか分からなかったため、Qiitaの記事( any型で諦めない React.EventCallback)にて参考にさせてもらいました。

以上コードの改善により、無事テキストフィールドに文字が反映されました!
React app