以前、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に反映されました!
今回は以上です!
↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック