はじめに
Reactでリロード時に接続可能のデバイスにアクセスしたいとき、コンポーネント内でnavigator.mediaDevices.enumerateDevices()
を使用した時、
(node:13144) UnhandledPromiseRejectionWarning: ReferenceError: navigator is not defined
......
と出てきて少しハマったので解決した方法を共有したいと思います。
navigatorとは
公式ドキュメントを参考にすると
Navigator インターフェイスは、ユーザーエージェントの状態や身元情報を表します。スクリプトからその情報を問い合わる、および活動を続けるためにそれら自体を登録することができます。
TL;DR
- 原因はコンポーネント直下でnavigatorを呼び出していることだった。
- 解決法はuseEffect内でnavigatorを実行すれば良い。
useEffectの処理の走るタイミングを知る
元々僕がやっていたコードは以下でした。(以下は間違いコードです。真似しないでください。)
//間違えコード
const Index: React.FC = () => {
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices.map((device) => {
//取得できたdeviceを使ってリスト形式に表示させたかった。
});
});
return (
<ul>
{/*取得できたdeviceをここでリスト表示させたい。*/}
</ul>
);
}
以上のコードだと、Indexコンポーネントがレンダリングされると同時のタイミングでnavigatorが呼び出され、処理がされてしまいます。
navigatorはユーザーエージェントの状態や身元情報を取得するので、まだレンダリングのタイミングではそれらの情報が取って来れなかったためにnavigator is not defined
エラーが出てしまったのだと考えました。
useEffectを復讐
今までの僕の勝手なuseEffectの認識(第二引数になにも入っていないデフォルトの状態)は
useEffectはただ画面をリロードするタイミングで処理がされるものなんでしょ!
とあまり深く考えずに認識してしまっていたので、今回のこの件で再確認することができました。
useEffectは、改めて公式ページを確認したところ、
useEffect は毎回のレンダー後に呼ばれるのか? その通りです! デフォルトでは、副作用関数は初回のレンダー時および毎回の更新時に呼び出されます。
以上のようにコンポーネントのレンダリングが完了後に実行される物でした。
よって、最終的なコードは以下のようになりました。
//正解コード
const Index: React.FC = () => {
{/*useEffect追加*/}
useEffect(() => {
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices.map((device) => {
//取得できたdeviceを使ってリスト形式に表示させたかった。
});
});
});
return (
<ul>
{/*取得できたdeviceをここでリスト表示させたい。*/}
</ul>
);
}
これでコンポーネントがレンダリングされた後にnavigatorを実行してあげることで値を取得することができ、エラーを解決することができました。
他により良い方法があれば教えていただけたら嬉しいです。