react-native-dark-mode でダークモードに対応する 


# はじめに
`react-native-dark-mode` というパッケージを使うことで、React Native アプリのダークモード対応をすることができた話です。
この記事ではiOS アプリについて書きます。 `react-native-dark-mode` はAndroid についても対応しており、公式のドキュメントに導入方法が記載されています。

[codemotionapps/react-native-dark-mode: Detect dark mode in React Native](https://github.com/codemotionapps/react-native-dark-mode) 

# 所感

`react-native-dark-mode` を利用することで、開発中のアプリの実装を大きく変更することなく、ダークモードを導入することができそうです。このライブラリはReact Hooks API を使って実装されており、ライブラリを利用する側は基本的に`useXxx` を使っていけばよいです。
React Native 公式っぽい書き方でスタイルを記述してきたプロジェクトであれば、スムーズに導入できそうな雰囲気があります。
(本記事の末尾に未対応の部分について書きましたが、がっつりプロダクションで使っていけそうかどうかは、もうちょっと検証したいです。)


# 検証環境
- React Native v0.62.2
- react-native-dark-mode v0.2.2

# インストール

```
$ npm install react-native-dark-mode
$ cd ios && pod install && cd ../
```


# 使い方

ほぼ公式のドキュメントの通りの内容ですが、少しコメントも書きます。

## useDarkMode

端末がダークモードになっている場合は `true` が得られます。

```jsx
import { useDarkMode } from 'react-native-dark-mode'

function Component() {
	const isDarkMode = useDarkMode() // true or false
	return <View style={{ backgroundColor: isDarkMode ? 'black' : 'white' }} />
}
```

## useDarkModeContext

`dark` か `light` が得られます。
オブジェクトのキーとして利用したい時に使えるでしょう。


```jsx
import { useDarkModeContext } from 'react-native-dark-mode'

const backgroundColors = {
	light: 'white',
	dark: 'black',
}

function Component() {
	const mode = useDarkModeContext() // 'light' or 'dark'
	const backgroundColor = backgroundColors[mode]
	return <View style={{ backgroundColor }} />
}
```

## DynamicStyleSheet, DynamicValue, useDynamicStyleSheet

React Native 標準の`StyleSheet.create` と書き方がほぼ同じですが、 `DynamicValue` を利用することによって、light mode の場合の値とdark mode の場合の値を定義することが出来ます。
スタイルを利用するコンポーネントで `useDynamicStyleSheet` を利用して、動的なスタイルを利用できるようにします。
 
```jsx
import { DynamicStyleSheet, DynamicValue, useDynamicStyleSheet } from 'react-native-dark-mode'

const dynamicStyles = new DynamicStyleSheet({
	container: {
		backgroundColor: new DynamicValue('white', 'black'), // dark mode なら黒背景
		flex: 1,
	},
	text: {
		color: new DynamicValue('black', 'white'), // dark mode なら白文字
		textAlign: 'center',
	},
})

function Component() {
	const styles = useDynamicStyleSheet(dynamicStyles)

	return (
		<View style={styles.container}>
			<Text style={styles.text}>My text</Text>
		</View>
	)
}
```

## useDynamicValue
関数内で動的な値を使いたい場合に利用します。
light mode の場合の値とdark mode の場合の値を定義することが出来ます。
画像やアイコン名を動的に切り替えたい場合に使えます。

```jsx
import { DynamicValue, useDynamicValue } from 'react-native-dark-mode'
const lightLogo = require('./light.png')
const darkLogo = require('./dark.png')
const logoUri = new DynamicValue(lightLogo, darkLogo)

function Logo() {
	const source = useDynamicValue(logoUri)
	return <Image source={source} />
}
```

```jsx
import { useDynamicValue } from 'react-native-dark-mode'

function Input() {
	const placeholderColor = useDynamicValue('black', 'white')
	return <TextInput placeholderTextColor={placeholderColor} />
}
```

## DarkModeProvider

特定のmode を子コンポーネントに渡す時に使えます。

```jsx
import { DarkModeProvider } from 'react-native-dark-mode'

function MyScreen() {
	return (
		<>
			{/* will be rendered using dark theme */}
			<DarkModeProvider mode="dark">
				<Component />
			</DarkModeProvider>

			{/* will be rendered using light theme */}
			<DarkModeProvider mode="light">
				<Component />
			</DarkModeProvider>

			{/* will be rendered using current theme */}
			<Component />
		</>
	)
}
```

パフォーマンス改善のため、アプリ全体を `DarkModeProvider` でラップすることが推奨されています。
ラップしなかった場合は、実行時にDebugger の警告が出る場合があります。

```jsx

function App() {
	return (
		<DarkModeProvider>
			{/* ... */}
		</DarkModeProvider>
	)
}
```

## initialMode

アプリ開始時のmode (light か dark) を取得します。

```jsx
import { initialMode } from 'react-native-dark-mode'

console.log('App started in', initialMode, 'mode')
```

## eventEmitter

mode の変更を購読することができます。

1. 端末で設定アプリを開く
2. 設定アプリでダークモードの設定を変更する
3. アプリに戻る

3 でアプリが再びフォアグラウンド状態になった時に、mode の切り替えが取得できます。


```jsx
import { eventEmitter } from 'react-native-dark-mode'

eventEmitter.on('currentModeChanged', newMode => {
	console.log('Switched to', newMode, 'mode')
})
```

# さいごに
上記の1~3 の手順でアプリを切り替えた時、 `TextInput` コンポーネントの `color` が切り替わらなかった...
解決できたらまた書きます。