Material UI は React のコンポーネントライブラリで様々な多機能なコンポーネントを提供してくれます。
Material-UI: A popular React UI framework
多数のコンポーネントを提供してくれますが、それでも開発で要求される全てを満たせることはなかなかありません。そういった時、独自にコンポーネントを作ることになります。この独自コンポーネントの内、フォーム内で用いるコンポーネントを Material UI のフォーム用コンポーネントを混ぜた時、後々困らないための方法を紹介します。
Material UI ではいくつかのフォーム用のまとまったコンポーネントがありますが大部分(全てを確認していませんがおそらく全部)はより素朴な Matrial UI コンポーネントで構成されています。例えば、 input[type=”text”] な入力欄を作る TextField コンポーネントは次の様になっています。
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import FormControl from "@material-ui/core/FormControl";
import FormHelperText from "@material-ui/core/FormHelperText";
import Input from "@material-ui/core/Input";
import InputLabel from "@material-ui/core/InputLabel";
import TextField from "@material-ui/core/TextField";
const useStyles = makeStyles(theme => ({
root: {
"& > *": {
margin: theme.spacing(1)
}
}
}));
export default function ComposedTextField() {
const [name, setName] = React.useState("Composed TextField");
const [error, setError] = React.useState(false);
const classes = useStyles();
const handleChange = event => {
setName(event.target.value);
};
return (
<form className={classes.root} noValidate autoComplete="off">
{/* TextField コンポーネント */}
<TextField
error={error}
helperText={error && "error"}
id="standard-secondary"
label="Name"
value={name}
onChange={handleChange}
/>
{/* TextField コンポーネントを再現するコンポーネント構成 */}
<FormControl error={error}>
<InputLabel htmlFor="component-simple">Name</InputLabel>
<Input id="component-simple" value={name} onChange={handleChange} />
<FormHelperText id="component-error-text">
{error && "error"}
</FormHelperText>
</FormControl>
<button type="button" onClick={() => setError(!error)}>
error切り替え
</button>
</form>
);
}
TextField は FormControl の中に InputLabel, Input, FormHelperText を入れたコンポーネントで再現できます。これを動かしたデモが次です。
エラー時の切り替えも含め全く同じ動作をします。Material UI コンポーネントと動作をそろえるために重要なのは FormControl, InputLabel, FormHelper などの制御用コンポーネントと制御内容を出力する細かいコンポーネントであると分かります。なんとなく見た目が整ったからヨシとして全くオリジナルのコンポーネントを作ったりすると、凝った動作内容を持つ Material UI コンポーネントと同じ動作をさせる段(バリデーション、アニメーションなど)で恐ろしく苦労します。
実際にこれを守った素の Material UI にないコンポーネントのデモが次です。
<FormControl error={error}>
<div style={{ display: "inline-flex", alignItems: "flex-end" }}>
<div>
<InputLabel htmlFor="component-simple">Name</InputLabel>
<Input id="component-simple" value={name} onChange={handleChange} />
</div>
<ClearIcon onClick={() => setName("")} />
</div>
<FormHelperText id="component-error-text">
{error && "error"}
</FormHelperText>
</FormControl>
入力をクリアするボタンを追加しました。Material UI にない部分を追加しましたが Material UI の機能を備えたままデザインも崩れていません。こういったものは Material UI の Component API から○○ Group, ○○ Content などのそれらしいコンポーネントパーツを集めることで実現できます。めんどくさがらずに目を通した上で改造に踏み切った方が後々の改修でひどい目にあいにくいです。