Electron & React & Redux & TypeScript アプリ作成ワークショップ をやってみた2
17995 ワード
https://qiita.com/y_ohr/items/5c18dd1621b5342a05ea の続き。
概要
以下をやってみた記録。良記事に感謝。
- https://qiita.com/EBIHARA_kenji/items/25e59f7132b96cb886f3 (前回済)
- https://qiita.com/EBIHARA_kenji/items/e6da1c3d6d16cf07b60a (前回済)
- https://qiita.com/EBIHARA_kenji/items/1a043794014dc2f3a7db (途中まで前回済)
環境
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
Node.jsとnpmのバージョン
$ node -v
v10.13.0
$ npm -v
6.4.1
ユーザー名入力画面の作成
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
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,
};
};
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
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;
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
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));
}
}
// 追加 -->
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
webpack実行とelectron起動
$ npm run build
$ npm start
動いた!
資産構成
感想
- ちゃんと動いて感動した
- Reduxに慣れが必要
- クラスや定数のimportが縦横無尽で追えなくなる。脳内マップが必要。
- ライブラリに追加した
uuid
やclone
は一般的なベストプラクティスなのだろうか?
- componentなどのUI関連クラスと、actionなどのRedux関連クラスは、もう少しフォルダ等を整理して、関心を分離できないのだろうか?こういうものなのだろうか。
- VSCodeでソースを整形しても、tslintに警告されないようにしたい。
uuid
やclone
は一般的なベストプラクティスなのだろうか?以上
Author And Source
この問題について(Electron & React & Redux & TypeScript アプリ作成ワークショップ をやってみた2), 我々は、より多くの情報をここで見つけました https://qiita.com/y_ohr/items/bc2173c20b9b903354e6著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .