モジュロ演算の魔法: Reactを使った画像切り替えの実装での応用方法

# モジュロ演算とは

簡単にいうと、**割り算の余り（モジュロ）を求める計算**です。例えば、

7 % 3 = 1 // 2余り**1**

2 % 4 = 1 // 0余り**2**

この余りの部分を求める計算になります。

## インデックスを計算する

ウェブアプリケーションで画像切り替えなどのコンポーネントを実装する場合、前後のアイテムへのナビゲーションはインデックスの計算にモジュロ演算が使われます。以下はReactを使用したサンプルコードです。

### 今回作ったもの

![Slide-App.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696287/d9f83071-a14d-f2fc-1de9-9dce6cc2cd40.gif)

### 使用技術

- React 18.2.0
- TypeScript
- Tailwind CSS
- Heroicons
- Vite

```TypeScript:App.tsx
import { useState } from 'react';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/16/solid';

interface Props {
  id: number;
  title: string;
  thumbnail: string;
}

const slides: Props[] = [
  {
    id: 1,
    title: "赤いサムネイル",
    thumbnail: "/thumb1.png"
  },
  {
    id: 2,
    title: "緑のサムネイル",
    thumbnail: "/thumb2.png"
  },
  {
    id: 3,
    title: "青いサムネイル",
    thumbnail: "/thumb3.png"
  },
]

function App() {
  const [currentIndex, setCurrentIndex] = useState(0);

  const nextSlide = () => {
    const nextIndex = (currentIndex + 1) % slides.length;
    setCurrentIndex(nextIndex);
  }

  const prevSlide = () => {
    const prevIndex = (currentIndex - 1 + slides.length) % slides.length;
    setCurrentIndex(prevIndex);
  }

  const currentSlide = slides[currentIndex];

  return (
    <>
      <div className="flex justify-center items-center min-h-screen">
        <div className="w-[500px] rounded-lg bg-white shadow-md py-6 px-8 relative">
          <div>
            <div className="mb-4">
              <img src={currentSlide.thumbnail} alt={currentSlide.title} />
            </div>
            <p>{currentSlide.title}</p>
          </div>
          <button
            type="button"
            className="absolute top-1/2 -left-5 flex justify-center items-center rounded-full w-10 h-10 border border-zinc-500 -translate-y-1/2 bg-white"
            onClick={prevSlide}
          >
            <span className="sr-only">前のスライド</span>
            <ArrowLeftIcon className="size-6 text-zinc-700" />
          </button>
          <button
            type="button"
            className="absolute top-1/2 -right-5 flex justify-center items-center rounded-full w-10 h-10 border border-zinc-500 -translate-y-1/2 bg-white"
            onClick={nextSlide}
          >
            <span className="sr-only">次のスライド</span>
            <ArrowRightIcon className="size-6 text-zinc-700" />
          </button>
        </div>
      </div>
    </>
  )
}

export default App
```

モジュロ演算は次のように使われています。

```TypeScript
const nextIndex = (currentIndex + 1) % slides.length;
```
```TypeScript
const prevIndex = (currentIndex - 1 + slides.length) % slides.length;
```

### 次のサムネイルのインデックスを計算する

nextIndexですが、次のサムネイルに移るときインデックスを1つ増やします。初回のサムネイルがインデックス0なので、次のサムネイルは1、その次のサムネイルは２となります。

しかし、現在のサムネイルが最後のサムネイル（インデックス2）だとすると、次のサムネイルは最初のサムネイル（インデックス0）に戻る必要があります。このため、モジュロ演算を使用します。

```TypeScript
const nextIndex = (currentIndex + 1) % slides.length;
```

例えば、現在のインデックスが2（最後のサムネイル）であれば、

```TypeScript
(2 + 1) % 3 = 3 % 3 = 0
```

これで最初のサムネイルに戻ります。

### 前のサムネイルのインデックスを計算する

prevIndexですが、前のサムネイルに移るときインデックスを1つ減らします。例えば、現在のサムネイルのインデックスが2だとすると、前はインデックス1、その前のサムネイルは0になります。

しかし、現在のサムネイルが最初のサムネイル（インデックス0）だとすると、次のサムネイルは最後のサムネイル（インデックス2）に戻る必要があります。このため、モジュロ演算を使用します。

```TypeScript
const prevIndex = (currentIndex - 1 + slides.length) % slides.length;
```

例えば、現在のインデックスが0（最初のサムネイル）であれば、

```TypeScript
(0 - 1 + 3) % 3 = 2 % 3 = 2
```

これで最後のサムネイルに戻ります。

## まとめ

インデックスを計算しなければならない場面は結構あるのかなと感じます。（例えば音楽プレイヤーの次の曲、前の曲の実装）

自分はドがつく文系出身ですので理解に苦労しました。自分のように計算が苦手な方に届いたらいいなと思います。

何か他に良い実装方法があれば教えていただければ幸いです。

## 参考

https://wa3.i-3-i.info/word15336.html