[React] 初心者講座02 <コンポーネントの書き方>
目標
- React Componentの定義方法が分かる
- styledを使用したComponentが書ける
基本的なComponentの書き方
React Componentは.tsx
ファイルで記述する。
JSX
という記法は変わらなくとも定義の仕方がいくつかある。
いくつかあるが僕の中のスタンダードはアロー関数
、React.VFC(React.FC)
を使って定義する。
(React v18以降はReact.VFC
は使わずReact.FC
を使うことになると思う。)
import React from 'react';
{/* props(Componentの引数)の名前はComponent名 + Propsとする */}
{/* 理由はprops自体をexportすることがよくあるから名前が被らないようにしておくため */}
type SampleComponentProps = {
{/* childrenが必要な場合はちゃんと定義する */}
children: React.ReactNode;
foo: string;
};
{/* Componentの型はReact.VFCで定義しておく */}
const SampleComponent: React.VFC<SampleComponentProps> = (props) => {
return (
<div>
<div>
{props.foo}
</div>
<div>
{props.children}
</div>
</div>
);
};
{/* default exportは別にしなくてもよい */}
{/* つまり export const SampleComponent って書いてもよい */}
export default AppHeader;
JSX.Element
を型定義として使用してはダメか? → 別によい。
ただ、どちらかに統一しといてほうが良さそうだなとは思う。
以下、JSX.Element
で型定義した例。
type SampleComponentProps = {
children: React.ReactNode;
};
const SampleComponent = (props: SampleComponentProps): JSX.Element => {
return (
<div>
{props.children}
</div>
);
};
export default SampleComponent;
Propsの書き方
別Componentのpropsを必要とする場合がよくある。
例として、メインタイトルとユーザ名を表示するヘッダComponentの実装を想定する。
Pickで必要なpropsを取り出す
まず、メインタイトルの実装が以下。
(見やすくするためにstyle記述はなしで、ただのspanにした。)
import React from 'react';
export type TitleLabelProps = {
label: string;
};
const TitleLabel: React.VFC<TitleLabelProps> => {
return <span>{props.label}</span>;
};
export default TitleLabel;
この<TitleLabel />
に加えてユーザ名を並べるヘッダComponentを以下のように定義する。
(styleは指定せず、とりあえず並べるだけにする。)
このときのHeaderProps
は以下のように定義する。
<TitleLabel />
を見に行ってlabel: string
をHeaderProps
にコピペはダメ。
import React from 'react';
import TitleLabel, { TitleLabelProps } from './TitleLabel';
export type HeaderProps = Pick<TitleLabelProps, 'label'> & {
userName: string;
{/* label: string; ← NG */}
};
const Header: React.VFC<HeaderProps> => {
return (
<div>
<TitleLabel label={props.label}>
<span>{props.userName}</span>
</div>
);
};
export default Header;
Pickで取りたいprops名が被っているとき
この<Header />
にサブタイトルが必要になったとする。
サブタイトルのComponentを以下のように定義した。
import React from 'react';
export type SubTitleLabelProps = {
label: string;
};
const SubTitleLabel: React.VFC<SubTitleLabelProps> => {
return <span>{props.label}</span>;
};
export default SubTitleLabel;
Pick<TitleLabelProps, 'label'> & Pick<SubTitleLabel, 'label'>
とは出来ない。
label
が重複してしまうので。
そんなときは以下のようにそれぞれ別名をつけて型を取得してくる。
import React from 'react';
import TitleLabel, { TitleLabelProps } from './TitleLabel';
import SubTitleLabel, { SubTitleLabelProps } from './SubTitleLabel';
export type HeaderProps = {
mainLabel: TitleLabelProps['label']; {/* stringになる */}
subLabel: SubTitleLabelProps['label']; {/* stringになる */}
userName: string;
};
const Header: React.VFC<HeaderProps> => {
return (
<div>
<TitleLabel label={props.mainLabel}>
<SubTitleLabel label={props.subLabel}>
<span>{props.userName}</span>
</div>
);
};
export default Header;
MUI(Material-UI)を元にしたComponentの書き方
書き方、というよりはルール。
MUIの<Button />
とかは基本的にPickでpropsを絞ったものを定義しておく。
そのまま使用するには汎用的すぎるので。
BaseButtonProps = ButtonProps & {...}
みたいに丸ごと使うことはしない。
でも自分は<Box />
はそのまま使ってたりする。
SwiftUIのVStack
HStack
のようなものを定義しといたほうが良さそうと思うこともある。
MUIのStack利用でもいいかも。
とりあえず以下、一例。
import React from 'react';
import Button, { ButtonProps } from '@mui/material/Button';
export type BaseButtonProps = Pick<ButtonProps, 'onClick'> & {
label: string;
};
const BaseButton: React.VFC<BaseButtonProps> => {
return (
<Button onClick={props.onClick}>
{props.label}
</Button>
);
};
export default BaseButton;
styled()を使用したComponent
公式にほとんど書いてある。
@emotion/styled
についても書いてあった。
emotion
とstyled-componens
の両方を使ったことがあるけど、@mui/system
のstyled
は使ったことがないので、本実装ではこちらを使ってみる。
多分、大差はない。
どのstyled
であれpropsの値を適用することはできる。
以下のような感じ。
import styled from '@emotion/styled';
type SampleProps = {
color?: string;
}
export const Sample = styled.div`
color: ${({color}: SampleProps): string => color ?? '#333333'};
`
styled()かsxか
MUI v5よりsx
というstyleを調整する仕組みが導入されている。公式はこちら。
どっち使えばいいの?ってなるけど公式には以下のように書いてあった。
When to use it?
- styled-components: the API is great to build components that need to support a wide variety of contexts. These components are used in many different parts of the application and support different combinations of props.
- sx prop: the API is great to apply one-off styles. It's called "utility" for this reason.
sx
は例外的なデザイン調整に使用すればいい感じなのかな?
見出しは基本10pxで、見出しComponentも作ったけど、ここだけ20pxだ。的な。
パフォーマンスも公式に書かれていてsx
よりstyled
の方が優れていそう。
Benchmark case | Code snippet | Time normalized |
---|---|---|
Render 1,000 components | <Div> |
120ms |
Render 1,000 styled components | <StyledDiv> |
160ms |
Render 1,000 Box | <Box sx={…}> |
370ms |
まあ、便利なので使っていく。
続き
前
Discussion