7839

雑草魂エンジニアブログ

【JS / React】ブラウザバックを制御する

今回、HP 内で意図的にブラウザバックを制御して、UX を向上させられるようにしたので、実装方法を紹介する。

ブラウザバックの制御に関して

ブラウザバックを制御するためには、セッション履歴を操作する必要がある。制御の流れは以下である。

  1. ページがレンダリングされたら、ダミーのセッション履歴を追加しておく
  2. ユーザーがブラウザの「戻る」ボタンを押す(1. でダミーの履歴を挿入していたので、実際にはブラウザバックが動作しない)
  3. popstateイベントが発火する
  4. 条件に応じて、以下を実行する
     a. ブラウザバックをする
     b. 任意の操作をする(再度、ブラウザバックをさせないように、ダミーのセッション履歴をまた追加しておく。)

ブラウザのセッション履歴の操作は、History API で実施する。

ダミーのセッション履歴は、History.pushState() を用いる。

history.pushState(null, null, null)

実装例

今回示す例では、同ページに、Step1 と Step2 のコンポーネントがあり、stateのstepに応じて、コンポーネントを切り替える。

ただし、step2 の場合、ブラウザバックを禁止して、ブラウザバックせずに、step1に戻るようにする。(特にスマホの操作において、ブラウザバックでstep1に戻るようにしておくと、UXが向上する。)

import { ReactElement, useEffect, useRef, useState } from 'react'
import Step1 from '~/components/Step1'
import Step2 from '~/components/Step2'

const Component = (): ReactElement => {
  const [step, setStep] = useState(1)
  const stepRef = useRef(null)

  useEffect(() => {
     history.pushState(null, null, null)
    stepRef.current = step
    window.addEventListener('popstate', overridePopstate, false)
    return () => window.removeEventListener('popstate', overridePopstate, false)
  }, [])

  useEffect(() => {
    stepRef.current = step
  }, [step])

  const overridePopstate = () => {
    if (stepRef.current === 2) {
      history.pushState(null, null, null)
      setStep(1)
    } else {
      history.back()
    }
  }

  return (
    <section>
      <div className="wrapper">
        {step === 1 ? (
          <Step1 goToNext={setStep(2)} />
        ) : (
          <Step2 goToBack={setStep(1)} />
        )}
      </div>
    </section>
  )
}

export default Component

特筆すべきは、リスナ内関数では state の変更を参照することができず、値渡しになっている。そのため、useRef を用いて、ref.current で参照するようにした。

まとめ

History API があることは知っていたが、今回初めて使って実装を行った。ブラウザバックに関して、意図したように制御ができてよかった。

それでは、ステキな開発ライフを。