14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React Native で Redux を導入する(React Native開発~第x回)

Last updated at Posted at 2019-02-07

概要

React Native の開発で、Reduxを導入します。

今回は、簡単なUIで Redux のデータフローの動作を確認しましょう。


前提条件


Redux とは

Redux は、ブラウザアプリでデータを state として管理するためのフレームワークです。

これは React Native のモバイルアプリ開発でも利用できます。

導入することで、複雑なアプリのデータの流れがわかりやすくなり、メンテナンス性が高まります。

React と合わせて使われることが多いですが、それぞれ独立したフレームワークなので、React 意外と組み合わせて使うことも可能です。

Redux について理解するには、下記のページが詳ので、一読するのをオススメします。


Redux ライブラリの導入

yarn でプロジェクトにインストールします。

> yarn add redux react-redux clone

react-redux は、React と Redux を接続するためのライブラリです。

clone は、指定したオブジェクトと同じ内容の別なオブジェクトを作成する(ディープクローンする)ライブラリです。

Redux の処理に利用します。

これらの TypeScript の型定義ファイルもインストールします。

> yarn add --dev @types/redux @types/react-redux @types/clone

TSLint の導入

TypeScript はコンパイル処理で、構文エラーをチェックしてくれますが、TSLint はそれ以上の指定したコーディングルール(例えば インデントのスペースの数や {} の位置、1行の最大行数など)をチェックするツールです。

チームで開発する際、Lint を導入しておけば、ルールに則った統一されたコードが生成されやすいです。

yarn で導入します。

> yarn add --dev tslint

ルールの定義ファイル tslint.json を作成します。

> yarn tslint --init

ルールは非常に多くのオプションがあります。

上のコマンドでは、おすすめのルールがすでに適用されたものが生成されます。

ここでは、これに加えて下記のルールを追加します。

tslint.json
{
    "defaultSeverity": "error",
    "extends": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {
        "quotemark": [ // 文字列リテラルのクォーテーション
            true,
            "single", //シングルクォーテーションを使う
            "jsx-double" // JSX のプロパティでは、ダブルクォーテーション
        ],
        "indent": [ // インデント
            true,
            "spaces", // スペースによるインデント 
            4 // スペースの数
        ]
    },
    "rulesDirectory": []
}

TSLint ルール一覧: https://palantir.github.io/tslint/rules/

Visual Studio Code の TSLint 拡張を入れておくと、コーディング中にルール違反がわかります。

image.png

各モジュールのディレクトリを作成する

Redux には、View(React Component)、Action、Reducer、State を言う要素に分けて開発します。それ毎にディレクトリを作るとソースコードの整理がしやすいです。

[project root]
 `- [src]
     |-[actions]
     |-[reducers]
     |-[states]
     |-[views]
        |-[components]
        `-[containers]

Power Shell の New-Item コマンドを利用すると、一気に作ることができます。

> New-Item -Path "./src/actions", "./src/reducers", "./src/states/", "./src/views/components", "./src/views/containers" -itemType Directory

Redux の動作を確認してみる

簡単な例で Redux の動作を確認します。

ここでは、画面で入力した文字を State で管理し、それを画面の別なコントロールに表示する、ということをやってみます。

State の作成

まず、扱うデータの型を定義します。ここでは単純に、文字列を1つだけ扱うオブジェクトの型とします。

src/states/IAppState.ts
interface IAppState {
    message: string;
}
export default IAppState;

Action の作成

アクションは、画面で入力した文字を受け取り、アクションオブジェクトを返します。

State にわたすには、dispatch を使いますがこれは、Component から送ります。

src/actions/AppAction.ts
import { Action } from 'redux';

/**
 * アクションを区別するための定数
 */
export const UPDATE_MESSAGE = 'UPDATE_MESSAGE';
/**
 * 変更したメッセージを Reducer に送るためのアクション
 */
export interface IUpdateMessageAction extends Action {
    message: string;
}
/**
 * メッセージを変更するアクションを作成する
 * @param message 変更するメッセージ
 */
export const createUpdateMessageAction = (message: string): IUpdateMessageAction => {
    return {
        message,
        type: UPDATE_MESSAGE,
    };
};

Reducer の作成

Reducer は、送られた Action を使って State を更新する処理を書きます。

src/reducers/AppReducer.ts
import clone from 'clone';
import { Action, Reducer } from 'redux';

import { IUpdateMessageAction, UPDATE_MESSAGE } from '../actions/AppActions';
import IAppState from '../states/IAppState';

/**
 * State の初期値
 */
const initState: IAppState = {
    message: '',
};

/**
 * Reducer 関数
 * @param state 現在のステート
 * @param action 渡されたアクション
 */
const appReducer: Reducer<IAppState> =
    (state: IAppState = initState, action: Action) => {
        let newState = state;
        switch (action.type) {
            case UPDATE_MESSAGE:
                {
                    // ステートを変更する場合は、別のオブジェクトを作成する
                    newState = clone(state);
                    const _action = action as IUpdateMessageAction;
                    newState.message = _action.message;
                }
                break;
            default:
                break;
        }
        // ここで返すオブジェクトが前回と異なるなら、関連する Component が再描画される。
        return newState;
    };

export default appReducer;

Store の登録

Store は、State の管理と、Reducer による変更を受け取り、Component に伝える役割があります。

Reducer は複数ある場合が多いので、それを combineReducers でまとめて、 createStore に渡して Store を作成します。

まずは、アプリ全体の State を Interface で定義します。この中に、上で作成した State (子State) を含めます。

src/IState.ts
import IAppState from './states/IAppState';

export default interface IState {
  app: IAppState;
}

Store の作成処理です。

src/Store.ts
import { combineReducers, createStore } from 'redux';

import IState from './IState';
import appReducer from './reducers/AppReducer';

const reducers = combineReducers<IState>({
  app: appReducer,
});

const store = createStore(reducers);

export default store;

Container Component の作成

ようやくになりますが、画面を設計します。

画面は、React Component クラスとして定義します。

ここで作成する Component は Redux の Store と接続する特別なものとなります。これを Container といいます。

React は外部から値をプロパティとして受け取り、その値を表示したり、値によって表示条件を変えたりするなどします。

また、画面の入力やボタンをタップなどのイベントのときに、画面の値を帰る場合は Acton を生成し、 dispatch 関数で Reducer に送ります。

ここでは、仮想DOM(JSX) を書くため、拡張子を '.tsx' とする必要があります。

さきに、ルートにある App.tsx を削除し、src/views/containers/App.tsx を作成します。

src/views/containers/App.tsx
import React, { Component } from 'react';
import { StyleProp, Text, TextInput, TextStyle, View, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { createUpdateMessageAction } from '../../actions/AppActions';
import IState from '../../IState';
import IAppState from '../../states/IAppState';

/**
 * UIのイベントで、Stateを変更するイベントハンドラを定義する
 */
interface IEvents {
    /**
     * メッセージが変更されたときの処理
     * @param message 変更後のメッセージ
     */
    onChangeMessage: (message: string) => void;
}

/**
 * この Container のプロパティを定義する
 */
interface IProps extends IEvents, IAppState {
}
/**
 * スタイルの定義
 */
const pageStyle: ViewStyle = {
    alignItems: 'center',
    flex: 1,
    justifyContent: 'center',
};
const inputStyle: StyleProp<TextStyle> = {
    borderColor: '#999',
    borderRadius: 5,
    borderWidth: 1,
    padding: 5,
    width: 200,
};
/**
 * サンプル画面 コンテナ
 */
class App extends Component<IProps> {
    /**
     * React の画面描画イベント
     */
    public render() {
        return (
            <View style={pageStyle}>
                <Text>はじめての React Native and Redux</Text>
                <TextInput
                    style={inputStyle}
                    placeholder="ここに値を入力してください"
                    onChangeText={this.props.onChangeMessage}>
                    {this.props.message}</TextInput>
                <Text>{this.props.message || ('ここに入力した文字が表示されます')}</Text>
            </View>
        );
    }
}
/**
 * ステートから、プロパティに値を適用する
 * @param state Redux のステート
 */
const mapStateToProps = (state: IState): IAppState => {
    return {
        message: state.app.message,
    };
};

/**
 * コンポーネントの UI イベントの処理を定義する
 * @param dispatch Reducer へ Action を送るための関数
 */
const mapDispatchToProps = (dispatch: Dispatch): IEvents => {
    return {
        onChangeMessage: (message: string) => {
            dispatch(createUpdateMessageAction(message));
        },
    };
};

// Redux の State と Component を接続する
export default connect(mapStateToProps, mapDispatchToProps)(App);

スタートページを作って指定する

最後に、開始するときの Component を index.js で指定します。

Container は、必ず という React-Redux モジュールが持っている コンポーネント の中で使わなくてはなりません。

ここでは下記のようにスタートページを作成します。

src/View/StartPage.tsx
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import store from '../Store';
import App from './containers/App';

export default class Start extends Component {
    public render() {
        return (
            // store プロパティには、上で作成した store オブジェクトを割り当てる
            <Provider store={store}>
                <App />
            </Provider>
        );
    }
}

index.js で Start.tsx を呼び出すように修正します。

index.js
/** @format */

import { AppRegistry } from 'react-native';
import StartPage from './src/views/StartPage';
import { name as appName } from './app.json';

AppRegistry.registerComponent(appName, () => StartPage);

起動して確認してみる

VSCode デバッグ機能で、"Debug Android" を実行してみましょう。

2019-02-07_09h40_02.gif

次回

次回は、ページ遷移について書きたいと思います。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?