LoginSignup
0
0

More than 5 years have passed since last update.

Electron & React & Redux & TypeScript アプリ作成ワークショップ をやってみた2

Posted at

https://qiita.com/y_ohr/items/5c18dd1621b5342a05ea の続き。

概要

以下をやってみた記録。良記事に感謝。

環境

Node.jsとnpmのバージョン
$ node -v
v10.13.0
$ npm -v
6.4.1

component の作成

ユーザー名入力画面の作成

ts/components/UserForm.tsx
import React from 'react';
import IUser from '../states/IUser';
import { TextBox } from './TextBox';

/**
 * ユーザ名を入力して表示する
 */
class UserForm extends React.Component<IUser, {}>{
    public render() {
        return (
            <div>
                <p>
                    <TextBox label="ユーザー名" type="text" value={this.props.name}
                        onChangeText={this.onChangeText} />
                </p>
                <p>名前: {this.props.name}</p>
            </div>
        );
    }

    private onChangeText = (value: string) => {
        // action や store ができてから書く
    }
}

action と action creator の作成

uuidインストール
$ npm install --save uuid && npm install --save-dev @types/uuid
ts/actions/UserNameEvents.ts
import Redux from 'redux';
import { v4 as UUID } from 'uuid';

/**
 * ユーザー名を変更するアクション・タイプ
 */
export const CHANGE_USER_NAME = UUID();

/**
 * ユーザー名を変更するアクション
 */
export interface IChangeUserNameAction extends Redux.Action {
    /** 変更する名前の文字列 */
    name: string;
}

/**
 * ユーザー名変更アクション・クリエイター
 * @param name 変更する名前の文字列
 * @return ユーザー名変更アクション
 */
export const createChangeUserNameAction: Redux.ActionCreator<IChangeUserNameAction> = (name: string) => {
    return {
        name,
        type: CHANGE_USER_NAME,
    };
};

reducer を作成する

cloneインストール
npm install --save clone && npm install --save-dev @types/clone
ts/reducers/UserReducer.ts
import Clone from 'clone';
import Redux from 'redux';

import { CHANGE_USER_NAME, IChangeUserNameAction } from '../actions/UserNameEvents';
import IUser, { initUser } from '../states/IUser';

export const UserReducer: Redux.Reducer<IUser> = (childState = initUser, action) => {
    let newChildState: IUser = childState;
    switch (action.type) {
        case CHANGE_USER_NAME:
            {
                newChildState = Clone(childState);
                newChildState.name = (action as IChangeUserNameAction).name;
            }
            break;
    }
    return newChildState;
};

store を作成する

ts/Store.ts
import { combineReducers, createStore } from 'redux';
import { UserReducer } from './reducers/UserReducer';
import IUser from './states/IUser';

/**
 * store のデータ型を定義する。(親state)
 * 
 * プロパティには、管理する child_state を指定する
 */
export interface IState {
    User: IUser;
    // state が増えたら足していく
}

// 複数の reducer を束ねる
const combinedReducers = combineReducers<IState>({
    User: UserReducer,
    // reducer が増えたら足していく
});

// グローバルオブジェクトとして、store を作成する。
export const store = createStore(combinedReducers);

// improt store from './Store' とアクセスできるように default として定義する
export default store;

store と component を連結させる

ts/components/UserForm.tsx
diff --git a/ts/components/UserForm.tsx b/ts/components/UserForm.tsx
index 36a22f1..6c3f614 100644
--- a/ts/components/UserForm.tsx
+++ b/ts/components/UserForm.tsx
@@ -1,5 +1,7 @@
 import React from 'react';
+import { connect, MapStateToPropsParam } from 'react-redux'; // 追加
 import IUser from '../states/IUser';
+import { IState } from '../Store'; // 追加
 import { TextBox } from './TextBox';

 /**
@@ -22,3 +24,9 @@ class UserForm extends React.Component<IUser, {}>{
         // action や store ができてから書く
     }
 }
+// 追加 -->
+const mapStateToProps = (state: IState) => {
+    return state.User;
+};
+export default connect(mapStateToProps)(UserForm);
+// <- 追加

component から action を reducer に送信する

ts/components/UserForm.tsx
diff --git a/ts/components/UserForm.tsx b/ts/components/UserForm.tsx
index 6c3f614..a51d8f8 100644
--- a/ts/components/UserForm.tsx
+++ b/ts/components/UserForm.tsx
@@ -1,7 +1,8 @@
 import React from 'react';
 import { connect, MapStateToPropsParam } from 'react-redux'; // 追加
 import IUser from '../states/IUser';
-import { IState } from '../Store'; // 追加
+import { createChangeUserNameAction } from '../actions/UserNameEvents'; // 追加
+import store, { IState } from '../Store'; // 変更
 import { TextBox } from './TextBox';

 /**
@@ -21,7 +22,7 @@ class UserForm extends React.Component<IUser, {}>{
     }

     private onChangeText = (value: string) => {
-        // action や store ができてから書く
+        store.dispatch(createChangeUserNameAction(value));
     }
 }
 // 追加 -->

HTMLへのレンダリング

ts/index.tsx
diff --git a/ts/index.tsx b/ts/index.tsx
index c25e4e5..58d6af1 100644
--- a/ts/index.tsx
+++ b/ts/index.tsx
@@ -1,9 +1,15 @@
 import React from 'react';
 import ReactDom from 'react-dom';
+import { Provider } from 'react-redux'; // 追加
+import UserForm from './components/UserForm'; // 追加
+import Store from './Store'; // 追加

 const container = document.getElementById('contents');
-
+// 変更 -->
 ReactDom.render(
-    <p>こんにちは、世界</p>,
+    <Provider store={Store}>
+        <UserForm />
+    </Provider>,
     container,
 );
+// 変更 <--

ビルドして動作確認する。

webpack実行とelectron起動
$ npm run build
$ npm start

動いた!

image.png

資産構成

image.png

感想

  • ちゃんと動いて感動した
  • Reduxに慣れが必要
  • クラスや定数のimportが縦横無尽で追えなくなる。脳内マップが必要。
  • ライブラリに追加したuuidcloneは一般的なベストプラクティスなのだろうか?
  • componentなどのUI関連クラスと、actionなどのRedux関連クラスは、もう少しフォルダ等を整理して、関心を分離できないのだろうか?こういうものなのだろうか。
  • VSCodeでソースを整形しても、tslintに警告されないようにしたい。

以上

0
0
1

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
0
0