最近、Node.js の EventEmitter というイベント処理用の機能を知ったので、React と組み合わせて簡単な実装サンプルを試してみました。
EventEmitter とは
独自のイベントを登録し、そのイベントがトリガーされたときに任意の処理を実行することができます。
emit と on を使ってイベントとトリガーされる処理を登録します。
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
それぞれのファイルの中身は下記です。
import { Link } from "react-router-dom";
export default function Page1() {
return (
<>
<p>Page1</p>
<Link to="/">戻る</Link>
</>
);
}
import { Link } from "react-router-dom";
export default function Page2() {
return (
<>
<p>Page2</p>
<Link to="/">戻る</Link>
</>
);
}
import { Link } from "react-router-dom";
export default function Page3() {
return (
<>
<p>Page3</p>
<Link to="/">戻る</Link>
</>
);
}
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>
</>
);
}
それぞれの画面に遷移できるように、ルーティングを定義します。
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 を下記のように書き換えます。
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 にイベントを発行するように修正します。
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>
</>
);
}
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>
</>
);
}
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 を活用することで色々できそうに思いました。