React公式ドキュメント「カスタムフックでロジックを再利用する」のコードサンプルの動きがどう見てもおかしいので直しました。具体的には、「フック間でリアクティブな値を渡す」のCodeSandboxのコードです。画面には3秒おきに通知が表示されます。ドキュメント本文の解説とどう関わるのかが、まったくわかりません。
通知はsetInterval()
で表示されていた
通知はサーバーとの接続をシミュレートするモジュール(chat.js
)に、setInterval()
で表示されていました。カスタムフックの話に絡みそうにありません。クリーンアップのclearInterval()
の呼び出しも含めて、さっくり除きましょう。
export function createConnection({ serverUrl, roomId }) {
// let intervalId;
let messageCallback;
return {
connect() {
console.log(
'✅ Connecting to "' + roomId + '" room at ' + serverUrl + "..."
);
/* clearInterval(intervalId);
intervalId = setInterval(() => {
}, 3000); */
},
disconnect() {
// clearInterval(intervalId);
messageCallback = null;
console.log(
'❌ Disconnected from "' + roomId + '" room at ' + serverUrl + ""
);
},
};
}
こうしたら、ドキュメントの解説もわかるようになりました(サンプル001)。画面に通知は表示されず、コンソールの出力で動きが確かめられます。画面のURLやチャットルームの選択を変更すると、チャットが再接続されるというサンプルです。
サンプル001■React: Passing reactive values between Hooks 01
https://codesandbox.io/s/react-passing-reactive-values-between-hooks-01-vw26qp
接続情報を通知で示す
公式ドキュメントの解説を読むにはこれで十分です。けれど、せっかく通知を表示するライブラリToastify JSがインストールされているのに、使わないのももったいないように思います。そこで、コンソール出力と同じ情報(connectionInfo
)を通知としても示すようにしてみました(サンプル002)。
export function createConnection({ serverUrl, roomId }) {
return {
connect() {
const connectionInfo = `✅ Connecting to "${roomId}" room at ${serverUrl}...`;
console.log(
// '✅ Connecting to "' + roomId + '" room at ' + serverUrl + "..."
connectionInfo
);
messageCallback && messageCallback(connectionInfo, 'white');
},
disconnect() {
const connectionInfo = `❌ Disconnected from "${roomId}" room at ${serverUrl}`;
// messageCallback = null;
console.log(
// '❌ Disconnected from "' + roomId + '" room at ' + serverUrl + ""
connectionInfo
);
messageCallback && messageCallback(connectionInfo, 'dark');
messageCallback = null;
},
};
}
export default function ChatRoom({ roomId }) {
useEffect(() => {
// connection.on("message", (msg) => {
connection.on("message", (msg, theme) => {
// showNotification('New message: ' + msg);
showNotification("New connection: " + msg, theme);
});
}, [roomId, serverUrl]);
}
サンプル002■React: Passing reactive values between Hooks 02
https://codesandbox.io/s/react-passing-reactive-values-between-hooks-02-mcp2ty
エフェクトのコードをカスタムフックに切り出す
もうひとつ、公式ドキュメントの主題であるカスタムフックへのロジックの切り出しまでやってしまいましょう。
コンポーネントChatRoom
のエフェクトを、そのまま新たなカスタムフックuseChatRoom
に移します。引数オブジェクトからは、serverUrl
とroomId
を受け取ることにしました。
import { useEffect } from 'react';
import { createConnection } from './chat.js';
import { showNotification } from './notifications.js';
export const useChatRoom = ({ serverUrl, roomId }) => {
useEffect(() => {
const options = { serverUrl, roomId };
const connection = createConnection(options);
connection.on('message', (msg, theme) => {
showNotification('New connection: ' + msg, theme);
});
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
};
ChatRoom
コンポーネントに、もうエフェクトは要りません。カスタムフックuseChatRoom
の引数オブジェクトに、serverUrl
とroomId
を渡して呼び出せば済むからです(サンプル003)。ロジックが切り分けられて、コードは読みやすくなったでしょう。
// import { useState, useEffect } from "react";
import { useState } from "react";
import { useChatRoom } from "./useChatRoom.js";
// import { createConnection } from "./chat.js";
// import { showNotification } from "./notifications.js";
export default function ChatRoom({ roomId }) {
/* useEffect(() => {
}, [roomId, serverUrl]); */
useChatRoom({ serverUrl, roomId });
}
サンプル003■React: Passing reactive values between Hooks 03
https://codesandbox.io/s/react-passing-reactive-values-between-hooks-03-4dpslt
TypeScriptを採り入れる
ご参考までに、TypeScriptを採り入れて型づけしたのがサンプル004です。少しコードも整理しました。
サンプル004■React: Passing reactive values between Hooks 04
https://codesandbox.io/s/react-passing-reactive-values-between-hooks-04-vmx36j
公式サンプルに、なぜあのような余分なコードが含まれていたのかは謎です。はじめにつくった作例が改定されたにしては、カスタムフックの主題には、絡むどころかかすりもしそうにありません。ブラウザでひと目見たら気づきそうなものです。おそらく、忙しかったのでしょう。