実現したいこと
関数コンポーネントでライフサイクルを実装する
困っていること
現在ちょっとしたタイピングゲームを制作しています。
クラスコンポーネントを用いて形にすることはできたのですが、
それを関数コンポーネントに書き換えてみたら以下の警告が出てしまいます。
Line 66:6: React Hook useEffect has a missing dependency: 'readInputKey'. Either include it or remove the dependency array react-hooks/exhaustive-deps
ウインドウにリスナーを設定したいのでそこは外せないし、第2引数にreadInputKeyを加えるとそこでも依存関係について警告がでてしまいます。
追記
とりあえず警告に出た依存関係を全て解消したら今度は挙動がおかしくなってしまいました。
# コード(部分的)
クラスコンポーネントでの記述
jsx
1import React from "react"; 2import styled from "styled-components"; 3import ChangeScreenButton from "../Share/ChangeScreenButton" 4import keyList from "../Share/keyList" 5 6// TODO 関数コンポーネント化 7// XXX 関数コンポーネント化する際に依存関係を解決しないといけない <=難しい 8class Game extends React.Component { 9 constructor(props) { 10 super(props); 11 this.maxSubject = 10; 12 this.state = { 13 subject: "", 14 inputContent: "", 15 correctCount: 0, 16 incorrectCount: 0, 17 elapsedTime_ms: 0, 18 }; 19 } 20 21 generateSubject() { 22 const rnd = Math.floor(Math.random() * keyList.length); 23 const subject = keyList[rnd]; 24 this.setState({subject: subject}); 25 } 26 27 readInputKey = (e) => { 28 let pressedKey = e.key 29 this.setState({inputContent: pressedKey}) 30 this.checkCorrectness(); 31 } 32 33 checkCorrectness() { 34 if(this.state.inputContent === this.state.subject) { 35 this.handleCorrect(); 36 } else { 37 this.handleIncorrect(); 38 } 39 } 40 41 handleCorrect() { 42 this.setState({ 43 correctCount: this.state.correctCount + 1, 44 inputContent: "" 45 }); 46 if(this.state.correctCount >= this.maxSubject) { 47 this.handleSubejctFinish(); 48 } else { 49 this.generateSubject(); 50 } 51 } 52 53 handleIncorrect() { 54 this.setState({ 55 incorrectCount: this.state.incorrectCount + 1, 56 }); 57 } 58 59 handleSubejctFinish() { 60 this.setState({elapsedTime_ms: Date.now() - this.state.elapsedTime_ms}) 61 this.props.sendResult(this.state.correctCount, this.state.incorrectCount, this.state.elapsedTime_ms); 62 this.props.handleScreen("result"); 63 } 64 65 componentDidMount() { 66 this.setState({elapsedTime_ms: Date.now()}) 67 this.generateSubject(); 68 window.addEventListener("keypress", this.readInputKey) 69 } 70 71 componentWillUnmount() { 72 window.removeEventListener("keypress", this.readInputKey) 73 } 74 75 render() { 76 return ( 77 <FlexContainer> 78 <Instruction>表示された数字または記号のキーを押してください</Instruction> 79 <Subject>{this.state.subject}</Subject> 80 <Footer> 81 <FlexP>問題数: {this.maxSubject}</FlexP> 82 <FlexP>正解数: {this.state.correctCount}</FlexP> 83 <ChangeScreenButton onClick={() => this.props.handleScreen("title")}>タイトルに戻る</ChangeScreenButton> 84 </Footer> 85 </FlexContainer> 86 ); 87 } 88} 89 90const FlexContainer = styled.div` 91 position: relative; 92 display: flex; 93 flex-direction: column; 94 align-items: center; 95 width: 100%; 96 height: 100%; 97` 98 99const Instruction = styled.p` 100 font-size: 20px; 101` 102 103const Subject = styled.p` 104 flex: 1; 105 font-size: 90px; 106` 107 108const Footer = styled.div` 109 width: 100%; 110 justify-self: flex-end; 111 display: flex; 112 justify-content: space-around; 113` 114 115const FlexP = styled.p` 116 display: inline-block; 117 margin: 0.2em; 118` 119 120 121export default Game; 122
関数コンポーネントに書き直してみる
jsx
1import React, {useState, useEffect} from "react"; 2import styled from "styled-components"; 3import ChangeScreenButton from "../Share/ChangeScreenButton" 4import keyList from "../Share/keyList" 5 6// TODO 関数コンポーネント化 7// XXX 関数コンポーネント化する際に依存関係を解決しないといけない <=難しい 8const Game = (props) => { 9 const [subject, setSubject] = useState(""); 10 const [inputContent, setContent] = useState(""); 11 const [correctCount, correctCountUp] = useState(0); 12 const [incorrectCount, incorrectCountUp] = useState(0); 13 const [elapsedTime_ms, setTime_ms] = useState(Date.now()); 14 15 const maxSubject = 10; 16 17 const generateSubject = () => { 18 const rnd = Math.floor(Math.random() * keyList.length); 19 const subject = keyList[rnd]; 20 setSubject(subject); 21 } 22 23 const readInputKey = (e) => { 24 let pressedKey = e.key 25 console.log(pressedKey); 26 setContent(pressedKey) 27 checkCorrectness(); 28 } 29 30 const checkCorrectness = () => { 31 if(inputContent === subject) { 32 handleCorrect(); 33 } else { 34 handleIncorrect(); 35 } 36 } 37 38 const handleCorrect = () => { 39 correctCountUp(correctCount + 1) 40 setContent("") 41 if(correctCount >= maxSubject) { 42 handleSubejctFinish(); 43 } else { 44 generateSubject(); 45 } 46 } 47 48 const handleIncorrect = () => { 49 incorrectCountUp(incorrectCount + 1); 50 } 51 52 const handleSubejctFinish = () => { 53 setTime_ms(Date.now() - elapsedTime_ms); 54 props.sendResult(correctCount, incorrectCount, elapsedTime_ms); 55 props.handleScreen("result"); 56 } 57 58 // 同じように書き換えたつもりがうまくいかない 59 useEffect(() => { 60 generateSubject(); 61 window.addEventListener("keypress", readInputKey); 62 console.log("キー入力受付開始"); 63 return () => { 64 window.removeEventListener("keypress", readInputKey); 65 console.log("キー入力受付終了"); 66 } 67 }, []) 68 69 return ( 70 <FlexContainer> 71 <Instruction>表示された数字または記号のキーを押してください</Instruction> 72 <Subject>{subject}</Subject> 73 <Footer> 74 <FlexP>問題数: {maxSubject}</FlexP> 75 <FlexP>正解数: {correctCount}</FlexP> 76 <ChangeScreenButton onClick={() => props.handleScreen("title")}>タイトルに戻る</ChangeScreenButton> 77 </Footer> 78 </FlexContainer> 79 ); 80} 81 82const FlexContainer = styled.div` 83 position: relative; 84 display: flex; 85 flex-direction: column; 86 align-items: center; 87 width: 100%; 88 height: 100%; 89` 90 91const Instruction = styled.p` 92 font-size: 20px; 93` 94 95const Subject = styled.p` 96 flex: 1; 97 font-size: 90px; 98` 99 100const Footer = styled.div` 101 width: 100%; 102 justify-self: flex-end; 103 display: flex; 104 justify-content: space-around; 105` 106 107const FlexP = styled.p` 108 display: inline-block; 109 margin: 0.2em; 110` 111 112 113export default Game; 114
依存関係をとりあえず解決
jsx
1import React, {useState, useEffect ,useCallback} from "react"; 2import styled from "styled-components"; 3import ChangeScreenButton from "../Share/ChangeScreenButton" 4import keyList from "../Share/keyList" 5 6// TODO 関数コンポーネント化 7// XXX 関数コンポーネント化する際に依存関係を解決しないといけない <=難しい 8const Game = (props) => { 9 const [subject, setSubject] = useState(""); 10 const [inputContent, setContent] = useState(""); 11 const [correctCount, correctCountUp] = useState(0); 12 const [incorrectCount, incorrectCountUp] = useState(0); 13 const [elapsedTime_ms, setTime_ms] = useState(Date.now()); 14 15 const maxSubject = 10; 16 17 const generateSubject = () => { 18 const rnd = Math.floor(Math.random() * keyList.length); 19 const subject = keyList[rnd]; 20 setSubject(subject); 21 } 22 23 const handleSubejctFinish = useCallback(() => { 24 setTime_ms(Date.now() - elapsedTime_ms); 25 props.sendResult(correctCount, incorrectCount, elapsedTime_ms); 26 props.handleScreen("result"); 27 }, [elapsedTime_ms, correctCount, incorrectCount, props]) 28 29 const handleCorrect = useCallback(() => { 30 correctCountUp(correctCount + 1) 31 setContent("") 32 if(correctCount >= maxSubject) { 33 handleSubejctFinish(); 34 } else { 35 generateSubject(); 36 } 37 }, [correctCount, handleSubejctFinish]) 38 39 const handleIncorrect = useCallback(() => { 40 incorrectCountUp(incorrectCount + 1); 41 }, [incorrectCount]) 42 43 const checkCorrectness = useCallback(() => { 44 if(inputContent === subject) { 45 handleCorrect(); 46 } else { 47 handleIncorrect(); 48 } 49 }, [inputContent, subject, handleCorrect, handleIncorrect]) 50 51 const readInputKey = useCallback((e) => { 52 let pressedKey = e.key 53 console.log(pressedKey); 54 setContent(pressedKey) 55 checkCorrectness(); 56 }, [checkCorrectness]) 57 58 useEffect(() => { 59 generateSubject(); 60 window.addEventListener("keypress", readInputKey); 61 console.log("キー入力受付開始"); 62 return () => { 63 window.removeEventListener("keypress", readInputKey); 64 console.log("キー入力受付終了"); 65 } 66 }, [readInputKey]) 67 68 return ( 69 <FlexContainer> 70 <Instruction>表示された数字または記号のキーを押してください</Instruction> 71 <Subject>{subject}</Subject> 72 <Footer> 73 <FlexP>問題数: {maxSubject}</FlexP> 74 <FlexP>正解数: {correctCount}</FlexP> 75 <ChangeScreenButton onClick={() => props.handleScreen("title")}>タイトルに戻る</ChangeScreenButton> 76 </Footer> 77 </FlexContainer> 78 ); 79} 80 81const FlexContainer = styled.div` 82 position: relative; 83 display: flex; 84 flex-direction: column; 85 align-items: center; 86 width: 100%; 87 height: 100%; 88` 89 90const Instruction = styled.p` 91 font-size: 20px; 92` 93 94const Subject = styled.p` 95 flex: 1; 96 font-size: 90px; 97` 98 99const Footer = styled.div` 100 width: 100%; 101 justify-self: flex-end; 102 display: flex; 103 justify-content: space-around; 104` 105 106const FlexP = styled.p` 107 display: inline-block; 108 margin: 0.2em; 109` 110 111 112export default Game; 113
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/08/04 05:52
2020/08/04 05:55
退会済みユーザー
2020/08/04 06:20