ReactNative Expoでアプリ開発入門してみる Part1

## 背景

React Nativeで開発はしてきたのですが、新しくなってきたExpoはあまり触ってなかったので、チュートリアルを走ってみます。


## 参考

https://docs.expo.dev/tutorial/create-your-first-app/


## プロジェクトの作成

`npx`でExpoのプロジェクトを作成します。

```sh
npx create-expo-app StickerSmash && cd StickerSmash
```

[アセット](https://docs.expo.dev/tutorial/create-your-first-app/#download-assets)をチュートリアルページからダウンロードしておきます。

ダウンロードしたファイルを、プロダクトの`assets`フォルダへ上書きします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3589058/febf0cbd-ee1b-0742-63f4-a506b764ef6e.png)


依存関係をインストールします。

```sh
$ npx expo install react-dom react-native-web @expo/webpack-config
```

下記のコマンドで開発サーバーを起動します。

```sh
npx expo start
```

起動後にターミナルで`w`を押すことで、WebブラウザでWebアプリが立ち上がります。
`a`でAndroidアプリ、`i`でiOSアプリが立ち上がります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3589058/a3a64511-3e1c-243b-3ca4-6c37945316df.png)


## 画面の構築

StickerSmashアプリの最初の画面を作成します。

下記の画面を作成していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3589058/c009c6c0-3ab9-542b-c710-d331010ce36d.png)

要素を分解すると、下記のようになります。
1つずつ実装していきます。

- 画面の背景色
- 画面中央に画像を表示
- 画面の下半分にボタンが2つ

### 画面の背景色

`styleプロパティ`は、JavaScript オブジェクトを値として受け入れる prop を受け入れます。
下記のbackgroundColorを`#25292e`へ変更します。

```diff_jsx:App.js
const styles = StyleSheet.create({
  container: {
    flex: 1,
+    backgroundColor: '#25292e',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
```

文字色が読みにくくなったので、文字色を変更します。
直接styleに書き込むことも可能です。


```diff_jsx:App.js
+      <Text style={{ color: '#fff' }}>~~~</Text>
```

### 画像を表示する
React Nativeの<Image>コンポーネントを使用して表示します。
静的アセットでもURLでも表示が可能です。Webのimgと同じ。

```diff_jsx:app.js
import { StatusBar } from 'expo-status-bar';
+ import { StyleSheet, View, Image } from 'react-native';

+ const PlaceholderImage = require('./assets/images/background-image.png');

export default function App() {
  return (
    <View style={styles.container}>
+      <View style={styles.imageContainer}>
+        <Image source={PlaceholderImage} style={styles.image} />
+      </View>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#25292e',
    alignItems: 'center',
  },
+  imageContainer: {
+    flex: 1,
+    paddingTop: 58,
+  },
+  image: {
+    width: 320,
+    height: 440,
+    borderRadius: 18,
+  },
});
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3589058/23aeb0b9-555e-f0a8-7c03-6fb9796d8562.png)


### コンポーネントをファイル分割する

ImageViewerを別ファイルへ切り出します。
ここはReactと同じなので割愛します。

```diff_jsx
      <View style={styles.imageContainer}>
+        <ImageViewer placeholderImageSource={PlaceholderImage} />
      </View>
```

### Pressableを使ってボタンを作る

下記のようなButton　Componentファイルを作ります。


```jsx:button.js
export default function Button({ label }) {
  return (
    <View style={styles.buttonContainer}>
      <Pressable style={styles.button} onPress={() => alert('You pressed a button.')}>
        <Text style={styles.buttonLabel}>{label}</Text>
      </Pressable>
    </View>
  );
}
```


```diff_jsx:app.js
      <View style={styles.footerContainer}>
+        <Button label="Choose a photo" />
+        <Button label="Use this photo" />
      </View>
```

下記のように、２つのButtonが追加されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3589058/4265a85b-1781-6e66-6d56-85f77f1cc2ca.png)


### Buttonコンポーネントを再利用しやすくする

ターミナルで `Control+C` を押して、開発サーバーを停止後、下記をターミナルで叩きます。

[expo/vecwetor-icons](https://docs.expo.dev/guides/icons/#expovector-icons)をインストールします。

```sh
npx expo install @expo/vector-icons
```

theme　Propsを受け取って、デザインをthemeに合わせて変更するように修正します。

```diff_jsx:button.js
import { StyleSheet, View, Pressable, Text } from 'react-native';
+ import FontAwesome from "@expo/vector-icons/FontAwesome";

+ export default function Button({ label,  theme }) {
+  if (theme === "primary") {
    return (
+      <View
      style={[styles.buttonContainer, { borderWidth: 4, borderColor: "#ffd33d", borderRadius: 18 }]}
      >
        <Pressable
          style={[styles.button, { backgroundColor: "#fff" }]}
          onPress={() => alert('You pressed a button.')}
        >
+          <FontAwesome
+            name="picture-o"
+            size={18}
+            color="#25292e"
+            style={styles.buttonIcon}
+          />
          <Text style={[styles.buttonLabel, { color: "#25292e" }]}>{label}</Text>
        </Pressable>
    </View>
    );
  }

  return (
    <View style={styles.buttonContainer}>
        <Pressable style={styles.button} onPress={() => alert('You pressed a button.')}>
          <Text style={styles.buttonLabel}>{label}</Text>
        </Pressable>
      </View>
  );
}

const styles = StyleSheet.create({
  // Styles from previous step remain unchanged.
});


```

```diff_jsx:App.js
      <View style={styles.footerContainer}>
+        <Button theme="primary" label="Choose a photo" />
        <Button label="Use this photo" />
      </View>
```

デザインの実装が完了しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3589058/6b193ca0-c6a6-b055-cea0-276ae469d960.png)