ReactNativeの体験としてStopWatchを作ってみた

## 作成の経緯
`ReactNative`のようなクロスプラットフォーム開発がどのようなものなのかが気になったので、作ってみようと思いました。
`ReactNative`を選んだのは、最近`React`を学んだからです。

また、友達と競走して作成をしました。

## 成果物

大体5時間ぐらいかけて作りました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696281/dad68e08-6ccc-7372-82e4-1415e78dc7ab.png" width="200"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696281/fa2fa53c-be99-a852-c0c4-502915ab70db.png" width="200"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696281/5e09c849-634e-223d-08ce-28f8efa4c5d9.png" width="200">

## 機能
* Startを押したとき
    * ストップウォッチが稼働する
    * StartボタンがStopボタンになる


* Stopを押したとき
    * ストップウォッチが止まる
    * StopボタンがStartボタンになり、Resetボタンが現れる


* Resetを押したとき
    * ストップウォッチが0になる
    * Resetボタンが消える

## ソースコード

###　主なファイル構造

```StopWatch
.
├── App.js
└─── components
    ├── Buttons.jsx
    ├── Buttons.jsx
    └── index.js

```

`index.js`はexport用のファイルになります

### App.js

ストップウォッチの主な機能を実装しています。

* [REACTNATIVE STOPWATCH作るの結構むずい](https://gitpress.io/@fukke0906/2020-04-22)
* [javascriptでストップウォッチを作ってみる。忘備録](https://qiita.com/ryomaDsakamoto/items/c49a9d4cd2017405af1b)

を参考にしつつ、自分で実装を考えてみました。

```js:App.js
import { StatusBar } from 'expo-status-bar';
import React, {useState} from 'react';
import {StyleSheet, View} from 'react-native';
import {StartBtn, StopBtn, ResetBtn, Display} from './components'

const App = () => {

  const [time, setTime] = useState(0)
  const [intervalId, setIntervalId] = useState()

  const startWatch = () => {
    const startTime = new Date().getTime()
    const id = setInterval(() => {
      setTime(new Date().getTime() - startTime + time)
    }, 10)
    setIntervalId(id)
  }

  const stopWatch = () => {
    clearInterval(intervalId)
    setIntervalId(null)
  }

  const resetWatch = () => {
    setTime(0)
  }

  return (
    <View style={styles.container}>
      <Display time={time} />
      <View style={styles.buttons}>
        {time === 0 && (
          <StartBtn startWatch={startWatch}/>
        )}
        {time > 0 && intervalId && (
            <StopBtn stopWatch={stopWatch} />
            )}
        {time > 0 && !intervalId && (
            <React.Fragment>
              <ResetBtn resetWatch={resetWatch}/>
              <StartBtn startWatch={startWatch}/>
            </React.Fragment>
        )}
      </View>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
    alignItems: 'center',
    justifyContent: 'center',
  },
  buttons: {
    position: 'relative',
  }
});

export default App
```

### Button.jsx

Buttonsのなかにそれぞれのボタンを作成しましたが、これは絶対に`Button`というコンポーネントを作って、その引数でスタートボタンやストップボタンを作成するようにすれば良かったと思います。

```jsx:Buttons.jsx
import React from 'react';
import {StyleSheet, Text,  TouchableOpacity } from 'react-native';

export function StartBtn({startWatch}) {
    return (
      <TouchableOpacity style={buttons.startBtn} onPress={() => startWatch()}>
        <Text style={btnText.start}>Start</Text>
      </TouchableOpacity>          
    )
}

export function StopBtn({stopWatch}) {
    return (
      <TouchableOpacity style={buttons.stopBtn} onPress={() => stopWatch()}>
        <Text style={btnText.stop}>Stop</Text>
      </TouchableOpacity>
    )
}

export function ResetBtn({resetWatch}) {
    return (
      <TouchableOpacity style={buttons.resetBtn} onPress={() => resetWatch()}>
        <Text style={btnText.reset}>Reset</Text>
      </TouchableOpacity>
    )
}

const btnBase = {
    position: 'absolute',
    width: 70,
    height: 70,
    backgroundColor: 'rgba(0,0,0,0.7)',
    borderRadius: 35,
    margin: 4,
    textAlign: 'center',
    justifyContent: 'center',
  }
  
const buttons = StyleSheet.create({
  startBtn: {
    ...btnBase,
    left: 96,
  },
  stopBtn: {
    ...btnBase,
    left: 96,
  },
  resetBtn: {
    ...btnBase,
    right: 96,
  },
})

const btnTextBase = {
  fontSize: 18,
  fontWeight: '500',
  textAlign: 'center',
}

const btnText = StyleSheet.create({
  start: {
    ...btnTextBase,
    color: '#1AFF1A',
  },
  stop: {
    ...btnTextBase,
    color: '#FF1A40',
  },
  reset: {
    ...btnTextBase,
    color: 'white',
  },
  lap: {
    ...btnTextBase,
    color: '#11FF44',
  }
})

```


### Display.jsx

`formatDisplay`メソッドで、ストップウォッチの表示部分の桁数を揃えるようにしています。

```jsx:Display.jsx
import React, {useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';

export function Display({time}) {

    const formatDisplay = (number) => {
        // 一桁の場合は、前に0をつける
        // 返却は文字列型になる
        if (number < 10) {
          number = '0' + number
        }
        return number
    }

    return(
        <View style={displays.displayContainer}>
            <Text style={displays.timer}>{formatDisplay(Math.floor(time / (60 * 1000)))}</Text>
            <Text style={displays.colon}>:</Text>
            <Text style={displays.timer}>{formatDisplay(Math.floor(time / (1000)) % 60)}</Text>
            <Text style={displays.colon}>:</Text>
            <Text style={displays.timer}>{formatDisplay(Math.floor((time % 1000) / 10))}</Text>
        </View>
    )
}

const displays = StyleSheet.create({
    displayContainer: {
      flexDirection: 'row',
      justifyContent: 'center',
    },
    timer: {
      width: 100,
      fontSize: 80
    },
    colon: {
      width: 20,
      fontSize: 80
    }
  });
```

## 作ってみた感想

簡単なアプリの場合は、調べたら出てきてしまうし、どちらかといえば見た目を整えるのが大変でした。
こういうものは、1時間の海外のチュートリアルを見て作ってしまった方がいいと思いました。

そのため、次に友達との競争をする場合は、簡単なアプリではなく、もう少しアイデアを出してから進めてみたいと思います。