• トップ
  • ブログ一覧
  • 【ReactNative】Reduxで状態管理
  • 【ReactNative】Reduxで状態管理

    はじめに

    React Native のアプリ開発で状態管理に Redux を使用しています。
    今回は、Redux の導入と基本的な使い方について備忘録としてまとめました。

    Redux とは

    Redux は、グローバルな状態管理のための JavaScript ライブラリです。
    主に React と ReactNative の状態管理に使用されます。

    React の状態管理方法としては他にも様々な選択肢がありますが、React に関する年次開発者調査を行っている Devographics の 2024 年調査によると、React の状態管理には useState に次いで Redux(Redux Toolkit)がよく使用されているという結果が出ています。
    このことから、React アプリケーションでの状態管理には Redux が広く使われていることが分かります。

    パッケージのインストール

    Redux のパッケージには、コアな部分だけを含んだ Redux と、コアに加えて主要なツールをまとめた Redux Toolkit があります。
    Redux を使用する際には、Redux Toolkit を利用することが推奨されています。

    また、React や React Native では、状態の更新時にコンポーネントを再レンダリングするために hooks を使用する必要があります。
    これをサポートするために、React Redux パッケージが提供されており、状態の管理とコンポーネント間のデータのやり取りを簡単にすることができます。

    以下のコマンドを実行し、Redux Toolkit と React Redux をインストールします。

    1npm install @reduxjs/toolkit react-redux

     

    Redux の基本的な使い方

    Redux の基本的な使い方として、ActivityIndicator の表示・非表示と色の状態管理を例として実装します。

    1. 状態を変更する関数を定義(createSlice 関数)

    まず、createSlice 関数を使って初期の状態を定義し、状態を変更するアクションを実装します。

    最初に、管理する状態の型を定義します。次に、createSlice を使って以下の要素を設定します

    • name: スライスの名前を設定します。
    • initialState: 状態の初期値を設定します。
    • reducers: 状態を変更するためのアクションを定義します。
    1// src/store/reducers/loadingSlice.ts
    2
    3import {createSlice, PayloadAction} from '@reduxjs/toolkit';
    4
    5// LoadingState 型を定義
    6interface LoadingState {
    7  isLoading: boolean; // ローディング状態(表示/非表示)
    8  color: string;      // ローディングの色
    9}
    10
    11// 初期状態を設定
    12const initialState: LoadingState = {
    13  isLoading: false, // 初期状態はローディング非表示
    14  color: '#00FF00', // 初期色は緑
    15};
    16
    17const loadingSlice = createSlice({
    18  name: 'loading', // スライスの名前
    19  initialState,    // 初期状態
    20  reducers: {
    21    // ローディング表示
    22    showLoading: state => {
    23      state.isLoading = true;
    24    },
    25    // ローディング非表示
    26    hideLoading: state => {
    27      state.isLoading = false;
    28    },
    29    // ローディングの色を変更
    30    toggleLoadingColor: (state, action: PayloadAction<boolean>) => {
    31      // trueの場合は青色、falseの場合は緑色に変更
    32      state.color = action.payload ? '#0000FF' : '#00FF00';
    33    },
    34  },
    35});
    36
    37export const {showLoading, hideLoading, toggleLoadingColor} =
    38  loadingSlice.actions;
    39
    40export default loadingSlice.reducer;

    2. store を作成

    次に、configureStore 関数を使用して、アプリケーションの状態を管理するための store を作成します。

    configureStore に先ほど作成した reducer を追加して作成します。
    さらに、状態を取得するために RootState 型を定義します。

    1// src/store/index.ts
    2
    3import {configureStore} from '@reduxjs/toolkit';
    4
    5import loadingReducer from './reducers/loadingSlice';
    6
    7// store の設定
    8export const store = configureStore({
    9  reducer: {
    10    // loading の状態を管理する reducer を追加
    11    loading: loadingReducer,
    12  },
    13});
    14
    15// RootState 型を定義
    16export type RootState = ReturnType<typeof store.getState>;

    3. ルートコンポーネントを Provider でラップ

    次に、Provider を使ってアプリケーション全体で状態を取得・更新できるようにします。

    先ほど作成した store を Provider に渡し、これでルートコンポーネントをラップします。
    これにより、Provier 配下のコンポーネントが Redux ストアにアクセスできるようになり、状態の取得や更新が可能になります。

    1// App.tsx
    2
    3import './src/assets/styles/global.css';
    4
    5import React from 'react';
    6import {Provider} from 'react-redux';
    7
    8import {store} from './src/store';
    9import Home from './src/screen/Home';
    10
    11function App(): React.JSX.Element {
    12  return (
    13    <Provider store={store}>
    14      <Home />
    15    </Provider>
    16  );
    17}
    18
    19export default App;

    4. 状態の取得と更新

    次は useSelector と useDispatch を使って実際に状態を操作します。

    状態に合わせて画面を変更するためには、useSelector を使用して状態を取得します。これにより、状態が変更されるたびに、コンポーネントが再レンダリングされます。

    状態を更新する場合には、useDispatch を使用して、dispatch 関数を呼び出し、必要なアクションを渡します。アクションが引数を取る場合は、その引数も一緒に渡します。

    1// src/screen/Home.tsx
    2
    3import React, {useState} from 'react';
    4import {View, ActivityIndicator, Button, Switch} from 'react-native';
    5import {useDispatch, useSelector} from 'react-redux';
    6
    7import {RootState} from '../store';
    8import {
    9  hideLoading,
    10  showLoading,
    11  toggleLoadingColor,
    12} from '../store/reducers/loadingSlice';
    13
    14const Home: React.FC = () => {
    15    const [isEnabled, setIsEnabled] = useState(false); // Switchの状態管理
    16    const dispatch = useDispatch();
    17  
    18    // Redux の状態を取得
    19    const isLoading = useSelector((state: RootState) => state.loading.isLoading);
    20    const loadingColor = useSelector((state: RootState) => state.loading.color);
    21  
    22    return (
    23      <View className="flex-1 justify-center items-center">
    24        <Button
    25          title="Loadingを表示"
    26          onPress={() => {
    27            // ローディングの表示
    28            dispatch(showLoading());
    29            // 3秒後にローディングを非表示
    30            setTimeout(() => dispatch(hideLoading()), 3000);
    31          }}
    32        />
    33        <Switch
    34          onValueChange={value => {
    35            // ローディングの色を変更
    36            dispatch(toggleLoadingColor(value));
    37            setIsEnabled(value);
    38          }}
    39          value={isEnabled}
    40        />
    41        {/* ローディングの表示・非表示を切り替え(Reduxから取得) */}
    42        {isLoading && (
    43          <>
    44            <View className="absolute inset-0 bg-black opacity-20" />
    45            {/* ローディングの色を変更(Reduxから取得) */}
    46            <ActivityIndicator
    47              className="absolute size-20 bg-gray-100 rounded-md"
    48              color={loadingColor}
    49            />
    50          </>
    51        )}
    52      </View>
    53    );
    54};
    55  
    56export default Home;
    以上が Redux の基本的な使い方です。

    便利な書き方

    基本的な使い方に加えて、役立ちそうな便利な書き方をいくつか見つけたので、ここにまとめて紹介します。

    useSelector を事前に型付け

    useSelector は使用時に毎回 RootState を型付けする必要がありますが、これを事前に型付けしておくことで、毎回の手間を省くことができます。

    withTypes を使って useAppDispatch と useAppSelector を事前に型付けし、アプリ全体で useDispatch と useSelector の代わりに使用するようにします。

    1// src/store/index.ts
    2
    3// ...
    4
    5// dispatchの型を定義
    6export type AppDispatch = typeof store.dispatch;
    7// stateの型を定義
    8export type RootState = ReturnType<typeof store.getState>;
    1// src/store/hooks.ts
    2
    3import {useDispatch, useSelector} from 'react-redux';
    4import {AppDispatch, RootState} from '.';
    5
    6// アプリ全体でuseDispatchとuseSelectorの代わりに使用する
    7export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
    8export const useAppSelector = useSelector.withTypes<RootState>();
    1// src/screen/Home.tsx
    2
    3// ...
    4
    5const dispatch = useAppDispatch();
    6
    7// Reduxの状態を取得
    8const isLoading = useAppSelector(state => state.loading.isLoading);
    9const loadingColor = useAppSelector(state => state.loading.color);
    10
    11// ...

    combineReducers で reducer をまとめる

    Redux には combineReducers 関数が用意されており、これを使用することで複数の reducer を一つにまとめることができます。

    combineReducers を使うことで、どの reducer がどの状態を管理しているのかが明確になり、コードがより読みやすく、保守しやすくなります。

    例えば、複数のモーダルの状態を Redux で管理している場合、それぞれのモーダルに対応する reducer を個別に作成し、combineReducers を使って一つの reducer にまとめることができます。

    1const modalReducers = combineReducers({
    2  successModalReducer,
    3  errorModalReducer,
    4});
    5
    6export const store = configureStore({
    7  reducer: {
    8    // ...
    9    modal: modalReducers,
    10  },
    11});

    おわりに

    本記事では、Redux の基本的な使い方についてまとめました。

    Redux は非常に便利な状態管理ツールですが、すべてのアプリケーションでの使用が推奨されているわけではありません。

    React の useState や useContext などのフックを活用することで、Redux のような状態管理処理を書くことは可能だと思うので、Redux を使用する際は、本当に必要かどうかをよく考えてから使用するようにしていきたいです。

    こちらの記事が何か参考になれば幸いです。 最後までお読みいただき、ありがとうございました!

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    いまむー(エンジニア)
    いまむー(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background