lelelemon’s blog

カメの歩みでのんびり学んでいます。

【React】EventEmitter でイベントを検知し任意の処理を実行する

最近、Node.js の EventEmitter というイベント処理用の機能を知ったので、React と組み合わせて簡単な実装サンプルを試してみました。

 

EventEmitter とは

独自のイベントを登録し、そのイベントがトリガーされたときに任意の処理を実行することができます。

 

emiton を使ってイベントとトリガーされる処理を登録します。

Node.jsの公式ページ では下記のように記載されています。

  • emit is used to trigger an event
  • on is used to add a callback function that's going to be executed when the event is triggered

この仕組みを利用したサンプルとして、React でいくつかのページを作成し、各ページが開かれた際にEventEmitterのイベントをトリガーする実装をしてみたいと思います。

 

環境構築

下記のコマンドで環境を検証用プロジェクトを作成します。

npx create-react-app event-emitter --template typescript

あとの手順でルーティングの機能を利用するので、react-router-dom を依存関係に追加します。

npm i react-router-dom

 

画面作成

検証用プロジェクトが作成できたので、いくつか画面を作成します。

cd event-emitter

mkdir src/component

touch src/component/page1.tsx

touch src/component/page2.tsx

touch src/component/page3.tsx

touch src/component/top.tsx

 

それぞれのファイルの中身は下記です。

page1.tsx
import { Link } from "react-router-dom";

export default function Page1() {
  return (
    <>
      <p>Page1</p>
      <Link to="/">戻る</Link>
    </>
  );
}
page2.tsx
import { Link } from "react-router-dom";

export default function Page2() {
  return (
    <>
      <p>Page2</p>
      <Link to="/">戻る</Link>
    </>
  );
}
page3.tsx
import { Link } from "react-router-dom";

export default function Page3() {
  return (
    <>
      <p>Page3</p>
      <Link to="/">戻る</Link>
    </>
  );
}
top.tsx
import { Link } from "react-router-dom";

export default function TopPage() {
  return (
    <>
      <p>
        <Link to="/page1">Page1へ</Link>
      </p>
      <p>
        <Link to="/page2">Page2へ</Link>
      </p>
      <p>
        <Link to="/page3">Page3へ</Link>
      </p>
    </>
  );
}

 

それぞれの画面に遷移できるように、ルーティングを定義します。

App.tsx
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import TopPage from "./component/top";
import Page1 from "./component/page1";
import Page2 from "./component/page2";
import Page3 from "./component/page3";

function App() {
  return (
    <Router>
      <div className="App">
        <Routes>
          <Route path="/" element={<TopPage />} />
          <Route path="/page1" element={<Page1 />} />
          <Route path="/page2" element={<Page2 />} />
          <Route path="/page3" element={<Page3 />} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

 

ここまでで、下記のコマンドでアプリを起動してみます。

npm start

トップ画面表示

Page1表示

上記が画面表示され、それぞれのページに遷移できます。

 

EventEmitterで任意のイベントをハンドリングする

ここから EventEmitterを使用してみます。

今回は、各ページが開かれた際に、そのページ名称をコンソールに出力するようなイベント処理を定義してみようと思います。

 

まずは下記のコマンドでEventEmitterを依存関係に追加します。

(EventEmitterは色々と改良が重ねられて、現在はバージョン3が最新の模様。)

npm i eventemitter3

mkdir src/eventContext

touch src/eventContext/EventContext.ts

 

EventContext で EventEmitter を生成し、useContext フックを使い、各コンポーネントからグローバルに参照できる構成としました。

EventContext.ts
import EventEmitter from "eventemitter3";
import { createContext } from "react";

const EventContext = createContext(new EventEmitter());

function triggerEvent(name: String) {
  console.log(`${name}が表示されました`);
}

const eventEmitter = new EventEmitter();

eventEmitter.on("pageEvent", triggerEvent);

export { EventContext, eventEmitter };

 

このEventContext で全体をラップすることで、配下のコンポーネントでEventEmitter を参照できるようになります。App.tsx を下記のように書き換えます。

App.tsx
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import TopPage from "./component/top";
import Page1 from "./component/page1";
import Page2 from "./component/page2";
import Page3 from "./component/page3";
import { EventContext, eventEmitter } from "./eventContext/EventContext";

function App() {
  return (
    <Router>
      <EventContext.Provider value={eventEmitter}>
        <div className="App">
          <Routes>
            <Route path="/" element={<TopPage />} />
            <Route path="/page1" element={<Page1 />} />
            <Route path="/page2" element={<Page2 />} />
            <Route path="/page3" element={<Page3 />} />
          </Routes>
        </div>
      </EventContext.Provider>
    </Router>
  );
}

export default App;

 

コンポーネントレンダリングが完了したら、EventEmitter にイベントを発行するように修正します。

 

Page1.tsx
import { useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { EventContext } from "../eventContext/EventContext";

export default function Page1() {
  const eventEmitter = useContext(EventContext);

  useEffect(() => {
    eventEmitter.emit("pageEvent", "Page1");
  }, [eventEmitter]);

  return (
    <>
      <p>Page1</p>
      <Link to="/">戻る</Link>
    </>
  );
}

 

Page2.tsx
import { useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { EventContext } from "../eventContext/EventContext";

export default function Page2() {
  const eventEmitter = useContext(EventContext);

  useEffect(() => {
    eventEmitter.emit("pageEvent", "Page2");
  }, [eventEmitter]);

  return (
    <>
      <p>Page2</p>
      <Link to="/">戻る</Link>
    </>
  );
}
Page3.tsx
import { useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { EventContext } from "../eventContext/EventContext";

export default function Page3() {
  const eventEmitter = useContext(EventContext);

  useEffect(() => {
    eventEmitter.emit("pageEvent", "Page3");
  }, [eventEmitter]);

  return (
    <>
      <p>Page3</p>
      <Link to="/">戻る</Link>
    </>
  );
}

 

これで完成です。

npm start

でアプリを立ち上げます。

 

トップページから、Page1, Page2, Page3 に画面遷移して、コンソールに下記のメッセージが表示されることを確認できます。

 

 

以上サンプルになります。

今回はただコンソールに出力するだけでしたが、各画面を開いたらGoogle Analytics に送信して画面表示数を計測したり、エラーが発生したら通知したり、など EventEmitter を活用することで色々できそうに思いました。