Component

コンポーネント

コンポーネント(Component)を分かりやすく?

Web開発でよく耳にする言葉だけど、これって一体ナンノコッチャ?って思いますよね?

まず、コンポーネントはUI(ユーザーインターフェース)を構成する独立した部品のことを指します。「部品」という感じで覚えるとよいかと思います。

それぞれのコンポーネントは、一つの特定の機能を持ち、それ自体が完結しているんです。そしてこれらのコンポーネントを組み合わせることで、ページ全体のUIを構築します。そのため、コンポーネント設計の原則として再利用可能で、独立性を持つことが重視されます。

それでは、この「コンポーネント」を例え話で考えてみましょう。レゴブロックを思い浮かべてみてください。各レゴブロックが一つのコンポーネントと考えることができます。それぞれのブロックは、一つ一つが形状や色を持ち、単体でも機能します。それらを組み合わせることで、様々な形状の建物や乗り物、動物などを作ることができます。それぞれのブロック(コンポーネント)が独立しているため、一つのブロックを変更したとしても他のブロックに影響を及ぼさず、さらに同じブロックを再利用することも可能です。

このように考えると、Next.jsやJamstack、TypeScriptを使って開発する際、コンポーネント設計の重要性が分かりますよね。コンポーネントを効果的に設計・使用することで、再利用性と保守性を高め、開発効率を向上させることができます。それって、すごくないですか?ここまで大丈夫ですか?

つまり、コンポーネントとは、UI の一部を構成する再利用可能な UI の塊(関数)で、React をベースとした Next.js では、コンポーネントを使ってアプリケーションの構造を組み立てることができるというわけです。また、TypeScript を使うことで、型安全性を確保しながらコンポーネントを開発できます。

React のコンポーネントは、次のようなタイミングで再描写が発生します。

  • props や内部状態が更新されたとき
  • コンポーネント内で参照している Context の値が更新されたとき
  • 親コンポーネントが再描写されたとき

上位のコンポーネントで再描写が発生すると、それ以下のコンポーネントも再描写が発生します。

import { Component } from 'next'

const Component: Component = () => {
  return <div>Component</div>
}

export default Component

Next.js でのコンポーネント作成

Next.js でコンポーネントを作成する際は、React のコンポーネント作成方法に従います。関数コンポーネントやクラスコンポーネントのどちらでも作成できますが、関数コンポーネントが推奨されます。以下では、関数コンポーネントを作成する方法を説明します。

関数コンポーネントの作成

componentsディレクトリを作成し、その中にコンポーネントファイル(例:MyComponent.tsx)を配置します。関数コンポーネントは、以下のように定義します。

// components/MyComponent.tsx
import React from 'react'

const MyComponent = () => {
  return (
    <div>
      <h1>Hello, World!</h1>
    </div>
  )
}

export default MyComponent

コンポーネントの利用

作成したコンポーネントは、他のページやコンポーネントでインポートして利用できます。

// pages/index.tsx
import React from 'react'
import MyComponent from 'components/MyComponent'

const HomePage = () => {
  return (
    <div>
      <MyComponent />
    </div>
  )
}

export default HomePage

TypeScript での型付け

TypeScript を使ってコンポーネントの型付けを行うことで、コードの品質を向上させることができます。以下では、プロパティ(props)とステート(state)の型付け方法を説明します。

プロパティ(props)の型付け

コンポーネントに渡すプロパティ(props)の型を定義することで、型安全性を確保できます。以下の例では、MyComponent コンポーネントに message プロパティを渡し、その型を定義しています。

// components/MyComponent.tsx
import React from 'react'

interface MyComponentProps {
  message: string
}

const MyComponent = ({ message }) => {
  return (
    <div>
      <h1>{message}</h1>
    </div>
  )
}

export default MyComponent

上記の例では、MyComponentPropsインターフェースを定義して、messageプロパティの型をstringに指定しています。その後、React.FC<MyComponentProps>を使って、コンポーネントのプロパティ型を指定します。

ステート(state)の型付け

コンポーネントの内部状態(state)も型付けすることで、型安全性を確保できます。以下の例では、useStateフックを使って、ステートの型を指定しています。

// components/Counter.tsx
import React, { useState } from 'react'

const Counter = () => {
  const [count, setCount] = useState<number>(0)

  const increment = () => {
    setCount(count + 1)
  }

  const decrement = () => {
    setCount(count - 1)
  }

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  )
}

export default Counter

上記の例では、useState<number>を使って、countステートの型をnumberに指定しています。

コンポーネントのコンポジション

コンポーネントを組み合わせて複雑な UI を構築することができます。コンポーネントを小さな単位で作成し、それらを組み合わせることで、コードの再利用性と保守性が向上します。以下の例では、HeaderFooterコンポーネントを作成し、それらをLayoutコンポーネントで組み合わせています。

// components/Header.tsx
import React from 'react'

const Header = () => {
  return (
    <header>
      <h1>My Website</h1>
    </header>
  )
}

export default Header

// components/Footer.tsx
import React from 'react'

const Footer = () => {
  return (
    <footer>
      <p>&copy; 2023 My Website</p>
    </footer>
  )
}

export default Footer

// components/Layout.tsx
import React from 'react'
import Header from './Header'
import Footer from './Footer'

interface LayoutProps {
  children: React.ReactNode
}

const Layout = ({ children }) => {
  return (
    <div>
      <Header />
      {children}
      <Footer />
    </div>
  )
}

export default Layout

上記の例では、Layoutコンポーネントを使って、ページ全体のレイアウトを組み立てることができます。