🐟

React の Functional Component で引数の記載方法を色々試す(TypeScript)

2021/06/20に公開

周知

2023/07 にリンクを修正しました、が、2021/06/20 の記事のため、かなり古い情報があります。
いずれ新しい記事書きます。古い情報であることを踏まえて読んでいただければ助かります。

概略

前回の記事[1]で React アプリ(TypeScript 版)の立ち上げをしてみました。
今回は, Create React App によって初期生成されたアプリをアロー関数に書き換えてみたり, Functional Component の記載方法をいろいろ試した内容をまとめています。

(参考)現在の環境の状態

Version 情報
React 17.0.1
Node.js 14.15.4
TypeScript 4.1.3

App.tsx をアロー関数に書き換える

Create React App で生成される App.tsx は Functional Component になっています。(function App() のあたりです)
React Hooks 登場により, Functional Component でも state 管理などできるようになり, 基本的には Functional Component 使っていくのが主流のようです。(以前は, Class Component が主)

生成時のApp.tsx

import React from 'react';

function App() {
  return (
    <div>
      {/* 省略 */}
    </div>
  );
}

export default App;

これをアロー関数で書き換えます。(アロー関数にする諸々のメリットは, this の取り回しやら簡潔化やらと聞いてますが詳細は他の方の記事をご覧ください)

アロー関数版App.tsx
import React from 'react';

// React.FC は省略可能
// const App = () => {
const App: React.FC = () => {
  return (
    <div>
      {/* 省略 */}
    </div>
  );
}

export default App;

変わったのは function App の行あたりです。コメント記載の通り, App 関数の型定義にあたる React.FC は省略可能です。ただ, 指定してあげる方が明瞭で好みです。

Functional Component の引数指定をいろいろ試す

以降はすべてアロー関数で記載していきます。
前述の App.tsx は, 引数を受けずに処理を実施しています。では, 引数を受ける Functional Component の書き方とは ... ?と思って調べるといろいろ出てきて困ったのでちょっとだけ試したものをまとめてみます。

引数を受けなくて良い場合のパターン

これは App.tsx と同じです。値を親から子へ受け渡す必要がない場合は, 以下のように書きます。
順に, React.FC の記載なし版と記載あり版です。

React.FCなし
const NoArgsComponent1 = () => {

  return (
    <div> NoArgsComponent1 </div>
  );
}
React.FCあり
const NoArgsComponent2: React.FC = () => {

  return (
    <div> NoArgsComponent2 </div>
  );
}
Component の使い方

上記 Functional Component を作成したあとに, Component を差し込みたいところで以下のように記載すれば OK。(例えば App.tsx

使い方
const App: React.FC = () => {
  return (
    <div>
      <NoArgsComponent1 />
      <NoArgsComponent2 />
    </div>
  );
}

引数を受けて何かしたいパターン

Component にデータを受け渡して表示させるなどの場合, Functional Component の引数を真面目に設定します。

単純なパターン

アロー関数の引数に指定します。(例は arg1, arg2 を設定)
props の型定義として, arg1/arg2 を宣言する形です。

引数内で指定
const WithArgsComponent = (props: { arg1: number, arg2: string }) => {

  return (
    <div>
      WithArgsComponent (arg1 = {props.arg1}, arg2 = {props.arg2})
    </div>
  );
}

React.FC を記載するパターン

上記単純なパターンを, React.FC を用いて記載すると以下になります。(React.FC のジェネリクスに, 引数で受けたいものの型定義を指定します)

React.FCを使用する
const WithArgsComponent: React.FC<{ arg1: number, arg2: string }> = (props) => {

  return (
    <div>
      WithArgsComponent (arg1 = {props.arg1}, arg2 = {props.arg2})
    </div>
  );
}

props については, オブジェクトの分割代入に置き換えて以下の書き方でも良いかと思います。

FCを使用する+分割代入で受け取り
const WithArgsComponent: React.FC<{ arg1: number, arg2: string }> = ({ arg1, arg2 }) => {

  return (
    <div>
      WithArgsComponent (arg1 = {arg1}, arg2 = {arg2})
    </div>
  );
}

また, ジェネリクスに指定した内容を type で定義してあげると見通しがよくなります。

引数の型定義を外だしする
type Props = {
  arg1: number;
  arg2: string;
}

const WithArgsComponent: React.FC<Props> = ({ arg1, arg2 }) => {

  return (
    <div>
      WithArgsComponent (arg1 = {arg1}, arg2 = {arg2})
    </div>
  );
}
Component の使い方

といろいろ書いたところで, 上記 Component を使用する際には以下のように記載します。(引き続き App.tsx
HTML の属性の記載風に, 引数が受け渡しできる感じですね。

使い方
const App: React.FC = () => {

  const num: number = 1;
  const str: string = 'sample';

  return (
    <div>
      <WithArgsComponent arg1={1} arg2='sample' /> {/* 値を直接入れる */}
      <WithArgsComponent arg1={num} arg2={str} /> {/* 変数値を入れる */}
    </div>
  );
}

Child Component を受け渡す場合

Component が Child Component を持つ場合, それらを受け渡すこともできます。
例えば, 以下のように対象の Component(WithArgsComponent) に Child Component が存在する場合,

App.tsx
const App: React.FC = () => {

  return (
    <div>
      <WithArgsComponent arg1={1} arg2='sample'>
        <div> Child ! </div>
      </WithArgsComponent>
    </div>
  );
}

WithArgsComponent を以下のように記載すれば, Child Component である <div> Child ! </div> を描画できるようになります。(React.FC を使わない単純な記載です。)

childrenを受け取る
const WithArgsComponent = (props: { arg1: number, arg2: string, children: React.ReactNode }) => {

  return (
    <div>
      WithArgsComponent (arg1 = {props.arg1}, arg2 = {props.arg2})
      {props.children}
    </div>
  );
}

WithArgsComponent 箇所の出力は以下な感じになります。

出力
<div>
  WithArgsComponent (arg1 = 1, arg2 = sample)
  <div> Child ! </div>
</div>

さて, 単純なパターンで記載しましたが, React.FC で記載すると以下のようになります。
さっきの単純な書き方例の children: React.ReactNode の記載がなくても分割代入で受け取れています。

React.FCを用いてchildlenを受け取る
type Props = {
  arg1: number;
  arg2: string;
}

const WithArgsComponent: React.FC<Props> = ({ arg1, arg2, children }) => {

  return (
    <div>
      WithArgsComponent (arg1 = {arg1}, arg2 = {arg2})
      {children}
    </div>
  );
}

この違いは, React.FCの定義を追いかけてみると, React.FC<P> -> FunctionComponent<P> -> PropsWithChildren<P> にたどり着きます。定義は以下です。

PropsWithChildren
type PropsWithChildren<P> = P & { children?: ReactNode }

React.FC のジェネリクスで指定される P と, children?: ReactNode& でつないでいます。なので, React.FC<Props> の記載のみで, children も受けることができるようになっています。

まとめ

Create React App で作成された App.tsx 内の Functional Component をアロー関数に書き換えつつ, Functional Component の引数の記載方法をいろいろ試してみました。
ちなみに Optional な引数を入れたい場合は { arg: number, optionalArg?: number } のように ? を使えば後は同じ感じです。

脚注
  1. Create React App(TypeScript)でアプリを起動して Material UI ちょこっと触るまで ↩︎

Discussion