ReactNavigation v.5 とReduxでタブのバッジ数を管理する

# この記事でやること
Reduxで管理する通知バッジ数をReactNativeのボトムタブに表示させる記事です。
**通知バッジ数はスクリーンをまたぐ変数なので、Reduxで管理するのが良い**かと思います。

こんな感じのやつです ↓↓ (この記事ではこれの簡易版を作ります)
![S__31916034.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371615/a58da30c-1238-ab81-3ddc-1b61c7284432.jpeg)


ReactNavigation v.4では、**ボトムタブのレンダリングのタイミングに癖があった(?)**ようです。
Reduxで状態を更新してもボトムタブに即時反映はされませんでした(僕の周りでも何人か言ってましたが、間違っていたら教えてください！)。
僕は無理やり**テキトーな変数を入れたNavigationActionsをボトムタブにdispatchすることで、無理やり再レンダリング**させて、即時反映させていました(本当はこの記事はそれを書く予定だった)。

しかしなんと、v.5ではそんな必要がなくなってました…！
ありがてぇ…！


#やりたいこと
画面から通知バッジ数を変更してボトムタブの数字に即時反映



#主な環境
- Expo 36.0.0
- react 16.9.0
- @react-navigation/bottom-tabs 5.0.5
- @react-navigation/native 5.0.5
- @react-navigation/stack 5.0.5
- react-redux 7.1.3
- redux 4.0.5

ReactNavigationはモジュールの移動が激しいですね。
[公式ドキュメント](https://reactnavigation.org/docs/en/getting-started.html)を読んで、必要なライブラリをインストールしていってください。


#画面構成
ホームスクリーンをStackにして、それをひとつのタブに対応させる単純な画面構成です。

#コード

##Redux

初期状態とreducerの定義をします

```javascript:src/reducers/index.js
const INITIAL_STATE = {
    badgeNumber:0,
}

const reducer = (state=INITIAL_STATE, action) => {
    switch (action.type){
        case "SET_BADGE_NUMBER":
            return {...state, badgeNumber:action.badgeNumber}
        default:
            return state;
    }
}

export default reducer
```

actionの定義をします

```javascript:src/actions/index.js
export const setBadgeNumber = badgeNumber => ({
    type:"SET_BADGE_NUMBER",
    badgeNumber
})
```

storeを作ります

```javascript:src/store.js
import { createStore } from 'redux'
import reducers from './reducers'

export default createStore(reducers)
```

## スクリーン
HomeScreenを作り、Reduxと繋げます。badgeNumber+1というテキストをタッチするとバッジ数がインクリメントされる仕様に。

```jsx:src/screens/Home.js
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { setBadgeNumber } from '../../src/actions'
import { connect } from 'react-redux'

const HomeScreen =({ badgeNumber,setBadgeNumber})=>{
  return(
    <View>
      <TouchableOpacity onPress={()=>setBadgeNumber(badgeNumber+1)}>
        <Text>badgeNumber + 1</Text>
      </TouchableOpacity>
    </View>
  )
}
const mapStateToProps = state => ({
  badgeNumber: state.badgeNumber
})

const mapDispatchToProps = {
  setBadgeNumber
}

 const Home = connect(
    mapStateToProps,
    mapDispatchToProps
  )(HomeScreen)

  export default Home
```

## ナビゲーション
ReactNavigationでナビゲーションを作ります。
StackとTabでHomeScreenをラップしていきます。


```jsx:App.js
import React from 'react';
import { Text } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Provider, connect } from 'react-redux'
import { setBadgeNumber } from './src/actions'
import store from './src/store'
import Home from './src/screens/Home'

// ここでStackをつくります()今回は1画面だけだけど
const Stack = createStackNavigator();

const HomeStack=()=>{
  return (
    <Stack.Navigator>
      <Stack.Screen name="Home" component={Home} />
    </Stack.Navigator>
  );
}


// ここでBottomTabを作ります(今回は上で作ったStack1つだけ)
const BottomTab = createBottomTabNavigator();

const MyBottomTab=()=>{
  return (
    <BottomTab.Navigator>
      <BottomTab.Screen 
        name="Home" component={HomeStack}
        options={{
          tabBarLabel: 'Home',
          tabBarIcon: () => (
            <Text>{store.getState().badgeNumber}</Text>
          ),
        }} />
    </BottomTab.Navigator>
  );
}


const Main=()=>{
  return(
    <NavigationContainer>
      <MyBottomTab>
      </MyBottomTab>
    </NavigationContainer>
  )
}

const mapStateToProps = state => ({
  badgeNumber: state.badgeNumber
})

const mapDispatchToProps = {
  setBadgeNumber
}

const ConnectedMain = connect(
  mapStateToProps,
  mapDispatchToProps
)(Main)

const App=()=>{
    return (
        <Provider store={store}>
            <ConnectedMain />
        </Provider>
    )
}
export default App

```

これで、HomeScreenのbadgeNumber+1をタッチすれば、ボトムタブの数字も更新されていくと思います。

# まとめ
基本に忠実なReduxの使い方、構成です。
上記のコードだけでタブに即時反映してくれるようになってとてもありがたいですね。
ReactNavigation v.5はv.4に比べて見通し良くなったと思います(前はcreateなんとかnavigatorみたいなのが何をやっているかわかりづらかった)。

今回は、HomeScreenから通知バッジ数を変更する仕様でしたが、プッシュ通知が来たらバッジ数をインクリメントすることも考えられるかなと。
僕も、プロジェクトにおいてWebsocket対応の優先順位はまだ低いと感じたときには、プッシュ通知が来たのをトリガーにバッジ数をインクリメントしています。
その場合、Focusされているのがどの画面か問わず、storeの状態を書き換えなければいけませんが、上記で言うMainコンポーネントの中でNotifications.addListenerを使ってハンドリングすると上手くいきます。

参考になれば幸いです！