コールバック関数「指定したタイミングで、これをやってね」

TwitterFacebookHatena

TL;DR

この記事では、Next.js と TypeScript を使ったコールバック関数の実装と利用について詳しく解説します。基本的な実装から、テクニカルな例までカバーしています。

開発環境 バージョン
Next.js 13.4.3
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

関数に関する記事

コールバック関数は「指定したタイミングで、これをやってね」

例えば、友達の家に遊びに行くときのシチュエーションを想像してみましょう。

あなたが友達の家に遊びに行くとき、その友達に「お昼になったら、私にお知らせしてね」と頼むとします。すると、その友達は、お昼になったらあなたに「お昼だよ」と教えてくれるでしょう。

この場合、あなたが友達にお昼の時間を教えてもらうことを頼んだのと同じように、プログラムの中では、ある関数が他の関数に対して「私が終わったら、あなたがやることをしてね」という要求をすることがあります。そして、その「あなたがやることをしてね」という部分がコールバック関数です。

つまり、コールバック関数とは、「私の仕事が終わったら、あなたがこれをやってね」という指示をする関数のことを言います。これを理解すると、プログラムを順番に実行するだけでなく、ある特定の時間や状況に合わせて特定の操作を行うことができるようになります。

まさに「タイミングぴったり、あなたの指示」というわけです!

このように、コールバック関数はプログラムの「頼みごと」のようなもので、その「頼みごと」が実行されるタイミングをコントロールすることができます。これがコールバック関数の基本的な理解です。

まとめると、コールバック関数とは、他の関数に引数として渡され、その関数の実行が完了した後で実行される関数です。コールバック関数を利用することで、非同期処理や関数の後処理など、より複雑な制御フローを可能にします。

どういったときに使われるのか?というと、主に非同期プログラミングで使用されます。たとえば、データベースからデータを取得するときや、ユーザーがボタンをクリックしたときなど、特定のイベントが発生したときに実行する関数を指定します。また、配列の操作(map、filter、reduce など)のような高階関数でコールバック関数が利用されることもあります。

たとえば、次のような基本的なコールバック関数の例を考えてみましょう。

type Callback = () => void

const executeCallback = (callback: Callback) => {
  console.log('Before callback execution')
  callback()
  console.log('After callback execution')
}

executeCallback(() => {
  console.log('This is a callback')
})

このコードは、TypeScript と Next.js を使ってコールバック関数を定義し、実行する例です。それぞれの部分について詳しく説明していきましょう。

type Callback = () => void

この行では、Callbackという名前の新しい型を定義しています。この型は、引数を取らずに何も返さない関数を表す() => voidという型を指します。

次に、

const executeCallback = (callback: Callback) => {
  console.log('Before callback execution')
  callback()
  console.log('After callback execution')
}

ここではexecuteCallbackという名前の関数を定義しています。この関数は、先ほど定義したCallback型の関数を引数として受け取ります。関数の内部では、まず'Before callback execution'というメッセージをログに出力し、次に引数として渡されたコールバック関数を実行します。コールバック関数が実行された後で、'After callback execution'というメッセージをログに出力します。

最後に、

executeCallback(() => {
  console.log('This is a callback')
})

この行では、executeCallback関数を呼び出しています。この関数には、console.log('This is a callback');というコードを実行するコールバック関数を引数として渡しています。

このコードを実行すると、次のような出力が得られます。

Before callback execution
This is a callback
After callback execution

これは、executeCallback関数がまず'Before callback execution'を出力し、次に渡されたコールバック関数を実行(ここで'This is a callback'が出力される)、最後に'After callback execution'を出力するからです。

Next.js で、コールバック関数を実装

Next.js と TypeScript を用いてコールバック関数を活用する一例として、状態を更新する際の後処理としてコールバック関数を利用する例を考えてみましょう。この例では、useState フックを用いて状態を管理し、その状態が更新された後にコールバック関数が実行されるようにします。

/src/components/CallbackExample.tsx

import { useState } from 'react'

type UpdateStateCallback = (value: boolean) => void

const CallbackExample = () => {
  const [state, setState] = useState(false)

  const updateStateWithCallback = (callback: UpdateStateCallback) => {
    setState((prevState) => {
      const newValue = !prevState
      callback(newValue)
      return newValue
    })
  }

  return (
    <div>
      <button onClick={() => updateStateWithCallback((newValue) => console.log(`State updated to ${newValue}`))}>Update state</button>
    </div>
  )
}

export default CallbackExample

このプログラムは、ボタンを押すと「状態」が更新される魔法の箱のようなものです。箱の中には、「状態」を表すランプがあり、それがオン(true)かオフ(false)かを表します。始めはランプはオフになっています。

const [state, setState] = useState(false)

この行は、ランプの状態(オンかオフか)と、その状態を変更するためのスイッチを作る部分です。

const updateStateWithCallback = (callback: UpdateStateCallback) => {
  setState((prevState) => {
    const newValue = !prevState
    callback(newValue)
    return newValue
  })
}

この部分は、ランプの状態を更新する方法を定義しています。その方法は、まず現在のランプの状態(prevState)を見て、それがオンならオフに、オフならオンにするというものです。そして、ランプの新しい状態を伝える役目を果たす「お知らせ係」(callback)に新しい状態を教えます。

<button onClick={() => updateStateWithCallback((newValue) => console.log(`State updated to ${newValue}`))}>Update state</button>

最後に、この部分はボタンです。このボタンを押すと、先ほど定義したランプの状態更新方法が実行されます。そして、「お知らせ係」(callback)にランプの新しい状態が通知され、その情報がログ(console)に書かれます。

このようにして、ボタンを押すたびにランプの状態が変わり、その変更がお知らせ係によって通知されるのです。この「お知らせ係」の部分が、プログラミングでいう「コールバック関数」に相当します。

高度なコールバック関数の実装

コールバック関数を利用することで、より複雑な制御フローを実装することも可能です。以下に、非同期処理の結果を扱うためのコールバック関数の高度な利用例を示します。

type AsyncCallback = (value: string) => Promise<void>

const asyncOperation = (callback: AsyncCallback) => {
  fetch('/api/data')
    .then((response) => response.json())
    .then((data) => callback(data))
    .catch((err) => console.error(err))
}

asyncOperation(async (data) => {
  console.log(`Received data: ${data}`)
  await doSomethingWithData(data)
})

このコードでは、asyncOperation 関数が API からデータを取得し、そのデータをコールバック関数に渡します。コールバック関数は非同期関数で、取得したデータを引数として受け取り、それを元に何らかの処理を非同期に実行します。

Emotion でのコールバック関数の活用

Emotion を用いてスタイリングを行う際にも、コールバック関数を活用することができます。たとえば、動的なスタイルを適用する際に、プロパティの値に応じて異なるスタイルを生成するための関数をコールバックとして利用することができます。

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const colorStyles = (color: string) => css`
  color: ${color};
`

const StyledComponent = ({ color }: { color: string }) => <div css={colorStyles(color)}>This is a styled component</div>

このコードでは、colorStyles 関数がコールバック関数として利用されています。この関数は指定された色を受け取り、それを元に CSS を生成します。

以上、Next.js と TypeScript を使ったコールバック関数の利用について解説しました。さまざまな状況でコールバック関数を活用することで、非同期処理や後処理、動的なスタイルの生成など、より柔軟かつ高度なプログラミングが可能になりますよね。この概念を理解して活用することで、より洗練されたコードを書くことができるようになります。

コールバック関数「指定したタイミングで、これをやってね」