はじめに
ReactでUIを構築するうえで、避けて通ることのできない最も重要なものがコンポーネントです。
コンポーネントを使わないReactはありえないと言っても過言ではないほどです。
そんなコンポーネントについて、概念や使い方を基礎からしっかり解説します。
コンポーネントの基礎
繰り返し使えるUIの必要性
以下のような画面があるとします。
ソースコードは次のようになっています。
const Main = () => {
return (
<>
<div style={{ margin: "5px" }}>
<button>ボタン</button>
</div>
<div style={{ margin: "5px" }}>
<button>ボタン</button>
</div>
<div style={{ margin: "5px" }}>
<button>ボタン</button>
</div>
</>
);
};
export default Main;
例えばmarginの値を一律で変更したり、他の要素を追加したりしたい場合、同じことを何度も書く必要があります。
そこで出てくるのが「コンポーネント」です。
コンポーネントとは
コンポーネントは「アプリのための再利用可能な UI 要素」であり、「マークアップを添えることができる JavaScript 関数」です。
要はJSXを戻り値として返す関数です。
コンポーネントの作成
まず、Button.jsというファイルを作ります。
ファイル内で、ボタンのJSXを返す関数を定義します。関数名の先頭は必ず大文字です。
関数名とファイル名は一致している必要はないですが、同じ方がわかりやすいです。
※私はアロー関数が好きなのでアロー関数で定義していますが、普通の関数宣言でも関数式でも問題ありません。
そして、作成した関数をエクスポートします。
// 関数名は必ず大文字
const Button = () => {
return (
<div style={{ margin: "5px" }}>
<button>ボタン</button>
</div>
);
};
export default Button;
これが、ReactにおけるUIの基本になります。
コンポーネントの使用
作成したコンポーネントをインポートすることで使えます。
使用の際はhtmlタグのように<>で関数名を囲みます。
このとき、<Button />または<Button></Button>のように、'/'(スラッシュ)を用いて必ずタグを閉じるようにします。<Button>ではエラーになります。
子要素がない場合には<Button />のようにします。
import Button from "./Button";
const Main = () => {
return (
<>
<Button />
<Button />
<Button />
</>
);
};
export default Main;
これで、「繰り返し使えるUIの必要性」で確認したのと同じ画面が出力できます。
以下のようにボタンのテキストを変更したり、marginを変更したりしたい場合は、Buttonコンポーネントを修正するだけでよいので同じことを何度も書く必要がなく、再利用性が高まります。
コンポーネントに値を受け渡す
柔軟性を高める
ボタンをコンポーネントとして切り分けることはできましたが、今の状態は一律で同じボタンなので使い勝手が悪いです。
ボタンのテキストや色などそれぞれ別々にしたいとなった場合、コンポーネントを別々に用意するよりも、コンポーネントの外側から値を受け渡して、振る舞いを変えられる方が汎用的で便利です。
そこで使用するのがpropsです。
propsとは
コンポーネントに渡す情報をpropsといいます。文字列だけでなく、オブジェクトや配列、関数など様々な情報をコンポーネントにpropsとして渡すことができます。
propsを受け渡す
コンポーネントに対して、渡したい情報をhtmlの属性のように指定します。プロパティ名(colorやtext)は自由に設定できます。
import Button from "./Button";
const Main = () => {
return (
<>
<Button color="red" text="赤いボタン" />
<Button color="blue" text="青いボタン" />
<Button color="green" text="緑のボタン" />
</>
);
};
export default Main;
propsを受け取る
propsはコンポーネントの関数にオブジェクトの形で引数として設定されます。propsを受け渡す際に指定した名前がプロパティ名になります。
const Button = (props) => {
return (
<div style={{ margin: "5px" }}>
<button style={{ color: props.color }}>{props.text}</button>
</div>
);
};
export default Button;
なお、propsはオブジェクトとして引数に設定されるので、分割代入を使って以下のように書くこともできます。
基本的には分割代入を使うのが一般的のようです。
const Button = ({ color, text }) => {
return (
<div style={{ margin: "5px" }}>
<button style={{ color: color }}>{text}</button>
</div>
);
};
export default Button;
画面は以下のように出力されます。
ボタンそれぞれに別の色とテキストが設定されています。
propsをうまく使うことで、より汎用的にコンポーネントを利用することができます。
コンポーネントのネスト
特別なprops: children
更にレイアウトを複雑にすることを考えます。
緑の枠でいろいろな要素を囲みたい場合、先程見た方法で要素を受け渡すだけでは限界があります。
緑の枠をコンポーネントとして定義して、ネストしたコンポーネントの中身を差し替えられれば理想的な挙動になります。
そこで登場するのが特別なpropsであるchildrenです。
コンポーネントの準備
まずは緑枠のコンポーネントを用意します。
const GreenBox = () => {
return (
<div
style={{
width: "200px",
margin: "5px",
padding: "5px",
border: "2px solid green",
}}
>
</div>
);
};
export default GreenBox;
このコンポーネントの<div>タグ内に子要素のコンポーネントを設定できればよさそうです。
あとで修正しますが、一旦この状態とします。
コンポーネントのネスト
次に緑枠のコンポーネントを呼び出します。GreenBoxタグにネストした子要素を設定しています。
import Button from "./Button";
import GreenBox from "./GreenBox";
const Main = () => {
return (
<>
<GreenBox>
<Button color="red" text="赤いボタン" />
</GreenBox>
<GreenBox>
<Button color="red" text="赤いボタン" />
<Button color="blue" text="青いボタン" />
</GreenBox>
<GreenBox>
<label>
名前:
<input type="text" />
</label>
</GreenBox>
<Button color="green" text="緑のボタン" />
</>
);
};
export default Main;
この子要素をGreenBoxコンポーネント側で受け取る必要があります。
childrenの使用
先ほど作成したGreenBoxコンポーネントに以下のように引数を追加します。
const GreenBox = ({ children }) => {
return (
<div
style={{
width: "200px",
margin: "5px",
padding: "5px",
border: "2px solid green",
}}
>
{children}
</div>
);
};
export default GreenBox;
propsのプロパティ名は基本的に自由ですが、childrenは定義済みの特別なプロパティ名です。
コンポーネントにネストされている子要素を丸ごと受け取ることができます。
ここでは分割代入を使用していますが、以下のようにしても同じことです。
const GreenBox = (props) => {
return (
<div
style={{
width: "200px",
margin: "5px",
padding: "5px",
border: "2px solid green",
}}
>
{props.children}
</div>
);
};
export default GreenBox;
画面は以下のように出力されます。
childrenによって、より柔軟にコンポーネントの中身を変えることができるようになりました。
まとめ
コンポーネントの概念や使い方を確認しました。
コンポーネントを使うことで、それぞれの部品ごとに処理を切り分けることができ、さらにpropsを使うことでより汎用性の高い部品を作ることができます。これにより再利用性が高まり、保守性も向上します。
今回は扱いませんでしたがuseStateなどのReact Hooksと組み合わせることで、さらにReactを使いこなすことができます。
また、Reactには多くのライブラリが存在していますが、コンポーネントの基本概念を理解しておくことでライブラリを使う上での理解も深まります。
次回はReact Hooksについて色々試した結果を書いていこうと思います。
最後までご覧いただきありがとうございました。