Reactのライブラリでたまに見る<Component.Content />みたいなやつを調べた。~雰囲気でJSを書いている仲間へ届け~


ことの発端

<Alert variant="danger" onClose={() => setShow(false)} dismissible>
  <Alert.Heading>...</Alert.Heading>
  <p>
    str...
  </p>
</Alert>

React Bootstrapを使用している時にこんなコードを目にし、私はこう思いました。

<Alert.Heading> ってなんだ...

export namespace Alert {
  export const Heading = (props) => <>...</>
}

みたいな実装?
でもこれだと<Alert {...props}>...</Alert>の箇所が書けない...

React Componentの話ではなくJavaScriptの話だった

ググるとStackOverflowに以下のようなコードが書いてありました。


// module.tsx
export const Component = () => <>...</>

Component.Body = () => <>...</>

// main.tsx
import ...

const Main = () => {
  return (
    <Component>
      <Component.Body />
    </Component>
  )
}

export default Main

なぜこのような書き方ができるのか?
雰囲気でJavaScriptを書いていた私にはわかりませんでした。

試しに同じことを普通の関数でやってみました。

const hello = (name: string) => `hello, ${name}`
hello.world = () => hello('world')

console.log(hello('john')) // hello, john
console.log(hello.world()) // hello, world

期待通り実行できてしまいました。
ここでよくラムダ式を使う私は普段意識していないあることを思い出しました。

JavaScriptの関数は第一級オブジェクトである

関数はオブジェクト、つまり、オブジェクトなのだからメソッドを生やせるわけです。

Type aliasを書くとこんな感じです。

type Hello = {
  (name: string): string;
  world(): string;
};

const hello: Hello = (name) => `hello, ${name}`;
hello.world = () => hello("world");

console.log(hello("john")); // hello, john
console.log(hello.world()); // hello, world