Gatsby.jsでスクロールに連動させて複数要素をフェードインさせる

# はじめに
以下の対応をするにあたりハマったのでメモです。
- Gatsby.jsにてスクロールに連動してふわっとフェードインしたい
- 複数要素に対応させたい

# 実装方法
react-intersection-observerを使用する方法もあるようですが、上手く複数要素に適応させることができなかったので、IntersectionObserverAPIを使用することにしました。

## useContext 使用

参考サイト：

https://zenn.dev/nami_y/articles/55f09eb1e00755

上記サイトでIntersectionObserverAPIをuseContextを利用してどこのコンポーネントでも使いまわせるようにする方法が書いてありました！

それを参考にさせてもらいました！
srcと並列階層にproviderフォルダを作成し、IntersectionObserverProvider.jsxを作成しました。

``` provider/IntersectionObserverProvider.jsx

import React, { createContext, useRef } from 'react'

export const ObserverContext = createContext({})

export function IntersectionObserverProvider(props) {
  const { children } = props
  const options = {
    rootMargin: '-20% 0px', // ここはオプションなので任意で
    once: false,
  }
  const targets = useRef([])

  const toTargets = el => {
    if (el && !targets.current?.includes(el)) {
      targets.current.push(el)
    }
  }

  return (
    <>
      <ObserverContext.Provider value={{ toTargets, targets }}>
        {children}
      </ObserverContext.Provider>
    </>
  )
}

```

参考サイトにはuseEffect以下のソースがありますが、私の環境の場合（Gatsby.jsのせいか、、？)はそれでは動かなかったので別に分けています。

ここでグローバルで使用できるtoTargetsとtargetsという変数を定義できました。


## IntersectionObserverを使用する関数

参考サイト内にあるuseEffect以下のソースはutils等に別の共通関数として切り出しました。


```utils/IntersectionObserver.js
export const handleObserver = (targets) => {
 
  targets.current.forEach(target => {
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.classList.add('show')
        }
      })
    })
    observer.observe(target)
  })

}

```

今回はスクロールがターゲットまで来たらshowというクラスを付与する感じです。


## Provider

useContextで実装したグローバルの変数を使用するには親のコンポーネントでProviderで囲む必要があります。
参考サイトはサイト全体に適応したいのでapp.jsxを親コンポーネントとしています。
ですが、Gatsby.jsにはapp.jsx的なファイルは存在しません。

GatsbyでContextを使いグローバルな状態管理を実装する方法は以下を参考にしました。

https://developers.altura.co.jp/posts/gatsby-global-context/


プロジェクトのルートにgatsby-browser.jsを作成し、そこに記述すれば良さそうです！

``` gatsby-browser.js
import React from "react"
import { IntersectionObserverProvider } from "./src/provider/IntersectionObserverProvider"

export const wrapRootElement = ({ element }) => (
  <IntersectionObserverProvider>{element}</IntersectionObserverProvider>
)

```

## 各コンポーネント

ここからは各コンポーネントで先ほど実装したものを適応していきます。
ターゲットにしたい各要素にref={toTargets}を追記。


```/pages/index.jsx
import React, { useEffect, useContext } from 'react'
import { ObserverContext } from "../provider/IntersectionObserverProvider";
import { handleObserver } from '../utils/IntersectionObserver'

export default () => {
  const { toTargets, targets } = useContext(ObserverContext);

  useEffect(() => {
    handleObserver(targets)
  }, [])

  return (
    <Layout>
      <section>
        <h2 ref={toTargets}>見出し</h2>
        <div ref={toTargets}>
       　　テキストテキストテキストテキストテキスト
　　　　　</div>
        <div ref={toTargets}>
          　・・・          
        </div>
      </section>
      <section>
        <h2 ref={toTargets}>見出し</h2>
　　　　　<p ref={toTargets}>テキストテキストテキストテキストテキスト</p>
      </section>
    </Layout>
  )
}

```


## 全体のおおまかな構成
実際には他にファイルはありますが、上記で出てきたファイルは以下のような構成になりました。

```
├── gatsby-browser.js
│
├── src
│     ├─ pages
│     │  └─ index.jsx
│     │  
      ├─ provider
      │   └─ IntersectionObserverProvider.jsx
      │
      ├─ utils
      │   └─ IntersectionObserver.js


```

## 最後に
IntersectionObserverAPIはJavaScriptなのでDOMを直接操作する例が多く、Reactで利用する方法を今回探して実装しました。
多くの記事はGatsby.js用ではなかったので、いろいろと異なり参考記事通りでは上手く動かなかったので、メモとして残すことにしました。

どなたかの参考になれば嬉しいです。