【Reactポモドーロアプリ】inputの入力値を別のcomponentで使用する
以前、JavaScriptでポモドーロタイマーを作成しましたが、現在そのアプリをReactへリプレイスしています。
React版でもユーザーが作業時間や休憩時間を設定できる機能を追加したのですが
実装までに苦戦したので、記録として残しておきます。
ただ、今回紹介する方法は師匠からのレビュー前のコードになります。
初心者の私が考えた処理手順になるので、もっといい方法はあると思います....
記録として残しておくという意味合いが強いので、その辺りご了承頂けますと幸いです。
※ポモドーロサイクルの実装もさらに苦戦したので、今度どこかのタイミングで記事にしたいと思います。順番が逆転してしまい、ごめんなさい....
ディレクトリ構成
現在のディレクトリ構成はこのようになっております。
赤枠に注目すると分かるのですが、今回はPresentational Component(componentsフォルダ) とContainer component(containersフォルダ)に役割を分離させています。
Presentational Component:View担当
Container component:ロジック担当
React/Redux環境ではプラクティスの一つらしいです。
こちらの記事が分かりやすいと思います!
[redux] Presentational / Container componentの分離 - react-redux.connect()のつかいかた - Qiita
それぞれ関心事をきちんと分離することでコンポーネントの再利用性を高めることができます。
ポモドーロタイマーはそこまで大きいアプリではないですが、
関心事を分けることでコードがスッキリして見やすいので分けることにしました。
実現したいこと
青枠内の作業時間に数字を入力後「OK」を押下と同時にその値が赤枠内に残り時間として反映される、ということ。
それぞれのviewは画像に記載した通り以下のPresentational componentが担当しています。
カウントダウンエリア(赤枠):CountDown.jsx
ユーザー入力エリア(青枠):userInput.jsx
作業時間の値は親コンポーネントである、App.jsxのstateで管理しようと考えました。
そして、赤枠内に表示されている残り時間はCountDown.jsxがStateで管理しているので
CountDown.jsxのStateの値をApp.jsxのstateの値で更新する、という流れです。
まとめると、値の流れとしては以下のようになります。
userInput.jsx→App.jsx→CountDown.jsx
①userInputで入力した値をAppに渡す
②AppのStateを更新
③更新したStateの値をCountDownへ渡す
④CountDownで受け取ったpropsでStateを更新させる
実際に書いたコード
順を追って説明していきます。
①userInput(view担当):onChange、onClickにコールバック関数を設定
青枠のviewを担当しているuserInput.jsxのコードは以下になります。
※作業時間のinputだけ抜粋しています。Material UIを導入していますが、Material UIについての詳しい説明は今回は省きます。
//components > userInput.jsx const UserInputComponent = ({ //Containers > userInput.jsxからpropsを受け取る handleInputWorkTime, inputWorkTime, callInputValueSet }) => { return ( <form className={classes.root} noValidate autoComplete="off"> <StyledInputField> <StyledTextField id="standard-basic" onChange={handleInputWorkTime} //inputの入力値を取得する関数 value={inputWorkTime} //ユーザー側から設定を変更できるように label="作業時間" /> </StyledInputField> <StyledButton variant="contained" type="submit" onClick={callInputValueSet} //App.jsxのStateの値を更新させる関数を呼ぶ関数 >OK</StyledButton> </form> ); }; export default UserInputComponent;
まずは、以下2つの関数を記述。どちらもこれからcontainers>UserInput.jsxで作成する関数です。
①inputの入力値を取得する関数
②App.jsxのStateの値を更新させる関数を呼ぶ関数
②UserInput(ロジック担当):onChange、onClickに設定したコールバック関数のプログラムを定義
//components > UserInput.jsx const UserInputContainer = (props) => { const[inputWorkTime, setInputWorkTime] = useState(25); const handleInputWorkTime = (e) => { //入力値を取得しState(UserInputContainerが持つstate)を更新 setInputWorkTime(e.target.value); } const callInputValueSet = (e) => { e.preventDefault(); //再レンダリングをキャンセル //AppのStateを更新する関数へ入力値を引数として渡す props.inputValueSet(inputWorkTime); } return ( <> //UserInputComponent へpropsを渡す <UserInputComponent handleInputWorkTime={handleInputWorkTime} inputWorkTime={inputWorkTime} callInputValueSet={callInputValueSet} /> </> ); };
③Appで関数の定義と、Countdown(ロジック担当)へpropsを渡す
//App.jsx const App = () => { const[workTime, setWorkTime] = useState(25); const inputValueSet = (inputWorkTime) => { setWorkTime(inputWorkTime); } return ( <div className="App"> <GlobalStyle /> <Header /> <div className="module-spacer" /> <CountdownContainer workTime={workTime} //入力値をpropsとして渡す /> <div className="module-spacer" /> <InputTimeDisplay/> <div className="module-spacer" /> <UserInputContainer inputValueSet={inputValueSet} //関数をpropsとして渡す /> </div> ); } export default App;
④Countdown(ロジック担当):useEffectの定義
//Containers > UserInput.jsx const CountdownContainer = (props) => { //残り時間はleftSecで管理 let[leftSec, setLeftSec] = useState(props.workTime*60); ~中略~ //workTimeが更新されたらleftSecが更新される useEffect(() => { setLeftSec(props.workTime*60); }, [props.workTime]) return ( <CountdownComponent workTime={props.workTime} // /> ); }
⑤Countdown(View担当):受け取ったpropsを表示
const CountdownComponent = ({leftSec}) => { return ( <> <StyledTimeDesplay>{secToMMSS(leftSec)}</StyledTimeDesplay> <div className="module-spacer" /> <div> <StyledButton variant="contained" onClick={handleStart} disabled={active}>START</StyledButton> <StyledButton variant="contained" onClick={handleStop} disabled={!active}>STOP</StyledButton> <StyledButton variant="contained" onClick={handleReset} disabled={!active}>RESET</StyledButton> </div> </> ); } export default CountdownComponent
※secToMMSSは秒を【○○:○○】の表示形式に変換する関数です。
これで、Inputに入力した値が、Countdownのviewに反映されました!
今回は以上です!
↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック