序章

Redux は、JavaScriptアプリの予測可能な状態コンテナーです。 Reduxを初めて使用する場合は、Reduxの紹介を参照することをお勧めします。

この記事では、ReactNativeアプリケーションでReduxを使用してユーザーデータを永続化する方法を学習します。 このアプリケーションは、接続されている友達の数を表示するHomeScreenと、追加する可能性のある友達のリストを表示するFriendsScreenを備えた模擬ソーシャルネットワークです。 Reduxを使用して、2つの画面間で状態を共有します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、 ReactNativeでReactNavigationでルーティングを使用する方法で説明されているトピックに基づいています。 プロジェクトがどのように機能するかについての詳細は、このチュートリアルを読むことをお勧めしますが、必須ではありません。

このチュートリアルは、ノードv14.7.0、npm v6.14.7、react v16.13.1、react-native v0.63.2、@react-navigation/native v5.7.3、 @react-navigation/stack v5.9.0、redux v4.0.5、およびreact-reduxv7.2.1。

ステップ1—プロジェクトのセットアップとインストールRedux

このチュートリアルでは、 ReactNativeでReactNavigationでルーティングを使用する方法のコードの修正バージョンを使用します。 開始するには、MySocialNetworkのクローンを作成します。

  1. git clone https://github.com/do-community/MySocialNetwork.git

次に、プロジェクトディレクトリに移動します。

  1. cd MySocialNetwork

gitブランチをredux-starterに変更します。

  1. git checkout redux-starter

次に、プロジェクトの依存関係をインストールします。

  1. npm install

次に、reduxおよびreact-reduxライブラリをプロジェクトにインストールします。

  1. npm install redux@4.0.5 react-redux@7.2.1

これでプロジェクトがセットアップされ、依存関係がインストールされました。

ステップ2—レデューサーを作成する

Reduxをアプリに接続するには、レデューサーアクションを作成する必要があります。

まず、友達レデューサーを作成します。 レデューサーは、前の状態とアクションを引数として取り、新しい状態を返す純粋関数です。 レデューサーは、変更に応じてアプリ全体で友達の現在の状態を最新の状態に保つのに役立ちます。

プロジェクトのルートレベルでFriendsReducer.jsファイルを作成します。

  1. nano FriendsReducer.js

次のコードを追加します。

FriendsReducer.js
import { combineReducers } from 'redux';

const INITIAL_STATE = {
  current: [],
  possible: [
    'Alice',
    'Bob',
    'Sammy',
  ],
};

const friendsReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    default:
      return state
  }
};

export default combineReducers({
  friends: friendsReducer
});

このファイルでは、ソーシャルネットワークに追加する可能性のある友達と一緒にINITIAL_STATE変数を作成します。 次に、friendsReducerfriendsというプロパティとしてエクスポートします。

レデューサーを配置したら、友達を追加する方法が必要になります。

ステップ3—アクションを作成する

アクションは、アプリケーションからReduxストアにデータを送信する情報のペイロードを表すJavaScriptオブジェクトです。

アクションには、タイプとオプションのペイロードがあります。 このチュートリアルでは、タイプはADD_FRIENDになり、ペイロードはcurrentフレンズ配列に追加するフレンズの配列インデックスになります。

プロジェクトのルートレベルでFriendsActions.jsファイルを作成します。

  1. nano FriendsActions.js

addFriendを追加します:

FriendsActions.js
export const addFriend = friendsIndex => (
  {
    type: 'ADD_FRIEND',
    payload: friendsIndex,
  }
);

ユーザーが友達をクリックすると、このコードはfriends.possible配列からfriendsIndexを取得します。 次に、そのインデックスを使用して、このフレンドをfriends.currentアレイに移動する必要があります。

FriendsReducer.jsに再度アクセスしてください:

  1. nano FriendsReducer.js

ADD_FRIENDを追加します:

FriendsReducer.js
// ...

const friendsReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case 'ADD_FRIEND':
      // Pulls current and possible out of previous state
      // We do not want to alter state directly in case
      // another action is altering it at the same time
      const {
        current,
        possible,
      } = state;

      // Pull friend out of friends.possible
      // Note that action.payload === friendIndex
      const addedFriend = possible.splice(action.payload, 1);

      // And put friend in friends.current
      current.push(addedFriend);

      // Finally, update the redux state
      const newState = { current, possible };
  
      return newState;

    default:
      return state
  }
};

// ...

このコードは、現在の友達と可能性のある友達を以前の状態から引き出します。 Array.splice()は、可能な友達の配列から友達を取得します。 Array.pushは、現在の友達の配列に友達を追加します。 変更が加えられると、状態が更新されます。

これで、レデューサーとアクションができました。 レデューサーをアプリに適用する必要があります。

ステップ4—アプリにレデューサーを追加する

React Reduxのプロバイダーコンポーネントを使用して、アプリのfriends状態を提供する必要があります。

App.jsを開きます:

  1. nano App.js

ProvidercreateStore、およびfriendsReducerをインポートします。

App.js
import 'react-native-gesture-handler';
import React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import friendsReducer from './FriendsReducer';
import HomeScreen from './HomeScreen';
import FriendsScreen from './FriendsScreen';

// ...

強調表示されたコードを追加して、createStoreおよびProviderに置き換えます。

App.js
// ...

const store = createStore(friendsReducer);

class App extends React.Component {
  // ...

  render() {
    return (
      <Provider store={store}>
        <NavigationContainer>
          <Stack.Navigator>
            <Stack.Screen
              name="Home"
              component={HomeScreen}
            />
            <Stack.Screen
              name="Friends"
              component={FriendsScreen}
            />
          </Stack.Navigator>
        </NavigationContainer>
      </Provider>
    )
  }
}

これで、アプリ内でfriendsにアクセスできますが、HomeScreenおよびFriendsScreenに追加する必要があります。

ステップ5—画面にReduxを追加する

このステップでは、mapStateToProps機能を使用して、画面からfriendsにアクセスできるようにします。 この関数は、2つの画面でstateFriendsReducerからpropsにマップします。

HomeScreen.jsから始めましょう。 HomeScreen.jsファイルを開きます。

  1. nano HomeScreen.js

HomeScreen.jsで強調表示されているコード行を追加して置き換えます。

HomeScreen.js
import React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Button } from 'react-native';

class HomeScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>You have (undefined) friends.</Text>

        <Button
          title="Add some friends"
          onPress={() =>
            this.props.navigation.navigate('Friends')
          }
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

const mapStateToProps = (state) => {
  const { friends } = state
  return { friends }
};

export default connect(mapStateToProps)(HomeScreen);

このコード変更により、react-reduxが追加され、friendsHomeScreenで使用できるようになります。

次に、現在の友達の値を追加します(this.props.friends.current):

HomeScreen.js
class HomeScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>You have { this.props.friends.current.length } friends.</Text>

        <Button
          title="Add some friends"
          onPress={() =>
            this.props.navigation.navigate('Friends')
          }
        />
      </View>
    );
  }
}

HomeScreenに現在の友達の数が表示されます。 これで、FriendsScreenに進むことができます。

FriendsScreen.jsを開きます:

  1. nano FriendsScreen.js

FriendsScreen.jsで強調表示されているコード行を追加して置き換えます。

FriendsScreen.js
import React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Button } from 'react-native';

class FriendsScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Add friends here!</Text>

        <Button
          title="Back to home"
          onPress={() =>
            this.props.navigation.navigate('Home')
          }
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

const mapStateToProps = (state) => {
  const { friends } = state
  return { friends }
};

export default connect(mapStateToProps)(FriendsScreen);

このコード変更により、react-reduxが追加され、friendsFriendsScreenで使用できるようになります。

可能な友達の値を追加します(props.friends.possible):

class FriendsScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Add friends here!</Text>

        {
          this.props.friends.possible.map((friend, index) => (
            <Button
              key={ friend }
              title={ `Add ${ friend }` }
            />
          ))
        }

        <Button
          title="Back to home"
          onPress={() =>
            this.props.navigation.navigate('Home')
          }
        />
      </View>
    );
  }
}

FriendsScreenに移動すると、レデューサーからのすべての可能な友達が表示されます。

最後に、新しいReduxaddFriendアクションをFriendsScreen.jsに追加します。

FriendsScreen.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { StyleSheet, Text, View, Button } from 'react-native';
import { addFriend } from './FriendsActions';

class FriendsScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Add friends here!</Text>

        {
          this.props.friends.possible.map((friend, index) => (
            <Button
              key={ friend }
              title={ `Add ${ friend }` }
              onPress={() =>
                this.props.addFriend(index)
              }
            />
          ))
        }

        <Button
          title="Back to home"
          onPress={() =>
            this.props.navigation.navigate('Home')
          }
        />
      </View>
    );
  }
}

// ...

const mapDispatchToProps = dispatch => (
  bindActionCreators({
    addFriend,
  }, dispatch)
);

export default connect(mapStateToProps, mapDispatchToProps)(FriendsScreen);

ソーシャルネットワークに2人の友達を追加し、HomeScreenに戻って、ユーザーの現在の友達の数を確認しましょう。

A series of screenshots depicting the HomeScreen and FriendsScreen as a user adds two friends and the array of possible friends and count of current friends channges.

これで、すべてのロジックがApp.jsからReduxに移動しました。これにより、特に認証やデータベース統合などのページや機能を追加するときに、アプリがはるかに柔軟になります。

まとめる前に、コードをクリーンアップしましょう。

ステップ6—クリーンアップ

Reduxを使用しているので、App.jsから渡した小道具は不要になります。

actionタイプを別のファイルに保存することで、さらにクリーンアップすることができます。

文字列'ADD_FRIEND'を、actionとその友人reducerの2か所で使用しています。 これは危険です。文字列を一方の場所で変更し、もう一方の場所では変更しないと、アプリケーションが破損する可能性があるためです。 アプリが大きくなるにつれて、これらすべてのactionタイプをtypes.jsというファイルに保持するのが理にかなっています。

ルートレベルでtypes.jsファイルを作成します。

  1. nano types.js

次のコードを追加します。

types.js
export const ADD_FRIEND = 'ADD_FRIEND';

次に、FriendsActions.jsに再度アクセスして、新しいADD_FRIENDを使用します。

nano FriendsActions.js

引用符で囲まれた'ADD_FRIEND'actionの変数ADD_FRIENDに変更します。

FriendsActions.js
import { ADD_FRIEND } from './types';

export const addFriend = friendsIndex => (
  {
    type: ADD_FRIEND,
    payload: friendsIndex,
  }
);

次に、FriendsReducer.jsに再度アクセスして、新しいADD_FRIENDも使用します。

  1. nano FriendsReducer.js

引用符で囲まれた'ADD_FRIEND'reducerの変数ADD_FRIENDに変更します。

FriendsReducer.js
import { combineReducers } from 'redux';
import { ADD_FRIEND } from './types';

// ...

const friendsReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case ADD_FRIEND:
      // ...

    default:
      return state;
  }
};

これにより、アプリケーションの脆弱性が低下します。 アプリケーションを開発するときは、コードを統合し、自分自身を繰り返さないようにする機会に注意する必要があります。

結論

このチュートリアルでは、reduxreducersactions、およびスケーラブルなデータ管理について説明しました。

データをデータベースと同期させることから、認証やユーザー権限の追跡まで、Reduxでできることはもっとたくさんあります。

このチュートリアルの完全なソースコードは、GitHubで入手できます。

Reactの詳細については、 React.js シリーズのコーディング方法をご覧になるか、Reactトピックページで演習やプログラミングプロジェクトを確認してください。