質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

938閲覧

【React】useMemoによる再レンダリングの抑制について

panda33

総合スコア5

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2022/01/11 17:27

編集2022/01/11 21:06

初めまして。
独学でプログラミングを勉強しており、現在Reactで初めてのWebサービス(タイピングゲーム)を作成しています。

今回、Reactの再レンダリングの抑制で詰まってしまったため、お力をお借りできませんでしょうか。

やりたいこと

useMemoなどを使用して、音声ファイルの再レンダリング(再取得)を抑制したい。

現在作っているサービスは、簡単なタイピングゲームで、単語がひとつづつ表示され、その単語を入力すると次の単語に表示が変わるという形になっています。
今回、新たに単語ごとに読み上げ音声が流れるようにしたところ、音声ファイルをAWS S3からたびたび再取得してしまい、動作が重くなってしまいました。そこで、ページ表示時に音声を取得したら、もう再レンダリングしないよう抑制したいです。

現状のコード

以下に、音声関係の主だったコードを抜き出しました。
(恐らく重要なのは、最初の6行ほどとは思いますが、念のため音声再生に使うコードまで記載しています。)
追記:1~6は全て同じ場所、App.tsxのexport default function App(props) {}内に記載してあります。

React

1//1.音声用の配列を定義 2const soundList = []; 3 4//2.AWS S3のURLと単語(props.Words)のidを使用して、音声ファイルのURLを作成&ロード 5 for (let i = 0; i < props.Words.length; i++) { 6 if (!props.Words[i]) continue; 7 soundList.push(new Audio(S3_BASE_URL + '+(' + props.Words[i].id + ').mp3') 8 ) 9 10//3.画面に表示されている単語と対応した音声ファイルを入れる定数を定義 11 const [audio, setAudio] = useState(soundList[0]); 12 13//audioのstateを更新する関数を定義 14 const updateSoundState = (wordId) => { 15 audio.pause(); 16 setAudio(soundList[wordId]); 17 } 18 19//4.単語の切り替えと同時に、音声も対応したものに切り替え(単語と音声ファイルの中から、変数numberで該当要素に切り替えています)。 20 useEffect(() => { 21 // audio更新 22 if(number<wordIdList.length){ 23 updateSoundState(number); 24 } 25 }, [number]); 26 27//5.音声ファイルを再生 28 useEffect(() => { 29 // audio再生 30 if(firstModalIsOpen==false){ 31 setTimeout( function() { 32 audio.currentTime = 0; //再生開始位置を先頭に戻す 33 audio.play(); //サウンドを再生 34 }, 10 ); 35 } 36 }, [audio]); 37 38

試したこと

下記のようにuseMemoを使用してみましたが、変化なしでした。
第2引数を変更するなど、試行錯誤してみましたが、うまくいきませんでした。

React

1//useMemoにて、配列に音声ファイルをpushするfor文を囲むも変化なし。 2const weightFunction = useMemo(() => { 3 for (let i = 0; i < props.Words.length; i++) { 4 if (!props.Words[i]) continue; 5 soundList.push(new Audio(S3_BASE_URL + '+(' + props.Words[i].id + ').mp3') 6 ) 7 } 8 return soundList; 9}, [soundList]);

また音声ファイルが再取得されるタイミングは、一定しておらず、法則性が良く分からないので原因もよくわかりません。

useMemoなどの再レンダリングの制御機能については、今回初めて勉強したため、的外れな質問となっていたら、申し訳ございません。

なにかお分かりの方がいらっしゃいましたら、ご教示いただけますと幸いです。
何卒宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2022/01/11 19:59

React (Hooks) のコードはどこ (関数の中か外か) に書いてあるかが重要です。1 〜 6 はどこに書きましたか? あと、「音声ファイルの再レンダリング(再取得)」という書き方に違和感が…。
panda33

2022/01/11 21:42 編集

ご検討いただき、ありがとうございます。 1~6は全て同じ場所、App.tsxのexport default function App(props) {}内にあります(コンポーネントに分けるのもまだ覚束ないため、その他全てのコードもここに記載しています)。 また、技術関連に触れ始めたのが最近のため、誤った言葉遣いをしてしまい恐縮です。
hoshi-takanori

2022/01/11 21:42

つまり 1 の const soundList = []; は App コンポーネントのローカル変数なんですね。それは当然レンダリングのたびに new Audio(〜) されるでしょうね。まずこいつを useState する必要があるかと。(でも、JS の Audio の使い方は知らないので、これ以上はよく分かりません…。)
hoshi-takanori

2022/01/11 21:57

↑ useState じゃなくて useMemo でしたね。このコンポーネントの props に Words という名前 (小文字の方がいいと思うけど…) で単語リストが渡ってくるなら、1 と 2 あわせてこんな感じでしょうか…。 const soundList = useMemo(() => { return props.Words.map(word => new Audio(S3_BASE_URL + '+(' + word.id + ').mp3')); }, props.Words);
panda33

2022/01/12 05:40

ありがとうございます! こちらのコードでうまく挙動させることができました。
guest

回答1

0

ベストアンサー

下記のようにuseMemoを使用してみましたが、変化なしでした。

soundListの配列は、useMemo関数内で作成しましょう。外側で作るのでは、配列インスタンスが描画ごとに入れ替わることとなり、メモとしての役割を全く果たしません。

javascript

1const soundList = useMemo(() => { 2 const ret = []; 3 for (let i = 0; i < props.Words.length; i++) { 4 if (!props.Words[i]) continue; 5 ret.push(new Audio(S3_BASE_URL + '+(' + props.Words[i].id + ').mp3') 6 ) 7}, [prop.Words]);

投稿2022/01/12 04:21

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2022/01/12 04:26

なお、useMemoの引数にprop.Wordsを書いていますが、これが頻繁に置き換わるような活用法では、やはりメモ化は有効となりません。
panda33

2022/01/12 05:42

ありがとうございます。 いただいた内容を使い、無事挙動させることができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問