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

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

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

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

Q&A

解決済

1回答

841閲覧

Reactでテトリスを作りたい(useEffectのInterval不具合)

cromalto

総合スコア12

React.js

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

0グッド

1クリップ

投稿2021/06/24 04:48

Reactの関数コンポーネントを使ってテトリスを作っているのですが、
キー入力時の useEffect と、インターバル設定時の useEffect がバッティングしてしまいます。

以下がコードです(一部)。

js

1// スタートボタンを押したときに落下描画する 2useEffect(() => { 3 if (started) { 4 const id = setInterval(() => { 5 let tmp = fallen 6 tmp.pop() 7 tmp.unshift([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 8 setFallen(tmp) 9 let draw_board = addMatrix(board, tmp) 10 drawMatrix(draw_board) 11 }, 1000); 12 13 return () => clearInterval(id); 14 } 15}, [started, board, fallen]); 16 17// キーボードイベントハンドラー 18useEffect(() => { 19 function handlekeydownEvent(event) { 20 const { key, keyCode } = event; 21 console.log(key, keyCode) 22 if (keyCode === 39) { // Right 23 let tmp = shiftRight(fallen) 24 setFallen(tmp) 25 let draw_board = addMatrix(board, tmp) 26 drawMatrix(draw_board) 27 } else if (keyCode === 37) { // Left 28 let tmp = shiftLeft(fallen) 29 setFallen(tmp) 30 let draw_board = addMatrix(board, tmp) 31 drawMatrix(draw_board) 32 } 33 } 34 35 document.addEventListener('keydown', handlekeydownEvent) 36 return () => { 37 document.removeEventListener('keydown', handlekeydownEvent) 38 } 39}, [board, fallen])

変数群
board 落ち切った動かないブロックの配列
fallen 現在落ちているブロックの配列

これらを足し合わせて描画しています。
インターバルで落ちるときや、移動したときには fallen を変更して、足し合わせるようにしています。

見て頂ければわかるのですが、移動してしまうと、fallen が変更されてしまい、インターバルがリセットされ、また1秒待つことになります。

これを、移動したとしてもインターバルを維持したままにしたいのですが、どうすればいいかわかりません。

お分かりの方がいらっしゃいましたら、ご教授いただければ幸いです。
よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

ちょっと複雑なアプリを作っていらっしゃるようなのでエスパーしてみます。codesandboxなどでコード全体を第三者が見れるようにしてもらうと回答がつきやすいかと思います。

質問の件は多分useEffectを全部走らせてるのが原因かなぁと思います。
useEffectはこう動いてほしいはずです。

  1. keyhandlerを設定するuseEffect
  2. 落下描画のタイマーのuseEffect
  3. 落下描画のタイマーのクリーンアップ
  4. keyhandlerのクリーンアップ

本来は1 -> 2<=>3 -> 4みたいに、2,3を繰り返してゲームが終わったときに4が走ってほしいのに、今はキーを押されたときに全部走ってるからバグになっているのだと思います。(繰り返しますが想像です)

なので解決策としては

js

1 2function ParentScreen(){ 3 const [key, setKey] = useState() 4 useEffect(()=>{ 5 // keyhandlerを設定 6    // setKey(keyCode)など? 7 }, []) 8 9 return <GameScreen pushedKey={key} /> 10} 11 12function GameScreen({pushedKey}){ 13 useEffect(()=>{ 14 // 落下描画のタイマーの設定 15 }, [...deps]) 16}

という感じになると思います。(一応同コンポーネントに2つのuseEffectをおいても同じことはできます)
今はkeyhandlerを設定するuseEffectで[board,fallen]といったdepsを指定していますが、ただのキーバインディングを設定するだけの機能が、これらゲーム内の状態を知らなければいけないのは不自然です。ので、keyhandlerを設定するuseEffectはdepsが空(ゲーム内の情報がない状態)になるはずです。

投稿2021/07/02 17:19

arark

総合スコア69

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

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

cromalto

2021/07/07 07:39

ありがとうございました!無事に解決できました! キーバインディングにDependencyはいらないという点を完全に見失っていました。 大変助かりましたorz
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問