https://qiita.com/y_ohr/items/7c6e9264a8b1f16651d2 の続き。
TypeScriptでReactアプリを作成します。
環境
$ yarn --version
1.12.3
$ create-react-app --version
2.1.8
更新可能なinput作成
前回の最後で作成したinputを、更新可能にします。
src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App bar="Hello!" />, document.getElementById('root'));
App.tsx
はdiff
で。
src/App.tsx
diff --git a/src/App.tsx b/src/App.tsx
index 14af72f..833f91e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -22,14 +22,31 @@ class App extends React.Component<Foo, {}> {
this.state = {
list: this.expressions
};
+
+ this.handleXChange = this.handleXChange.bind(this);
+ this.handleYChange = this.handleYChange.bind(this);
+ }
+
+ private handleXChange(i: number, e: any) {
+ this.expressions[i].x = parseInt(e.target.value);
+ this.setState({
+ list: this.expressions
+ });
+ }
+
+ private handleYChange(i: number, e: any) {
+ this.expressions[i].y = parseInt(e.target.value);
+ this.setState({
+ list: this.expressions
+ });
}
render() {
return this.expressions.map((d, i) =>
<div key={d.n}>
- <input type="number" value={d.x} />
+ <input type="number" value={d.x} onChange={(e) => this.handleXChange(i, e)} />
<span>+</span>
- <input type="number" value={d.y} />
+ <input type="number" value={d.y} onChange={(e) => this.handleYChange(i, e)} />
<span>=</span>
<input type="number" value={d.x + d.y} />
</div>
VSCodeでデバッグ
少し脱線して、デバッグ設定。
.vscode/launch.json
を追加。
/.vscode/launch.json
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "1.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
tsconfig.json
にて"sourceMap": true
を追加。
tsconfig.json
diff --git a/tsconfig.json b/tsconfig.json
index 377f514..0219ac3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,9 +17,10 @@
"resolveJsonModule": true,
"noEmit": true,
"jsx": "preserve",
- "isolatedModules": true
+ "isolatedModules": true,
+ "sourceMap": true
},
"include": [
"src"
]
-}
+}
参考:https://code.visualstudio.com/docs/typescript/typescript-debugging#_clientside-debugging
enum,interface,abstract class
せっかくなので、なるべく型を使うようにして実装します。
式の演算子をenum
化。
src/App.tsx
enum Operator {
plus = "+",
minus = "-",
times = "×",
divide = "÷",
}
interface
に上記enum
追加。
src/App.tsx
interface IExpression {
n: number;
x: number;
o: Operator;
y: number;
Caluculate(): number;
}
abstract class
で上記interface
を実装。setter
,getter
でもいけた。
src/App.tsx
abstract class ExpressionBase implements IExpression {
private _n: number = 0;
get n(): number { return this._n; }
set n(value: number) { this._n = value; }
private _x: number = 0;
get x(): number { return this._x; }
set x(value: number) { this._x = value; }
private _o: Operator = Operator.plus;
get o(): Operator { return this._o; }
set o(value: Operator) { this._o = value; }
private _y: number = 0;
get y(): number { return this._y; }
set y(value: number) { this._y = value; }
public constructor(n: number, x: number, o: Operator, y: number) {
this._n = n;
this._x = x;
this._o = o;
this._y = y;
}
public Caluculate(): number {
switch (this.o) {
case Operator.plus: return this.x + this.y; break;
case Operator.minus: return this.x - this.y; break;
case Operator.times: return this.x * this.y; break;
case Operator.divide: return this.x / this.y; break;
default: return 0;
}
}
}
実体classは今のところ空。
src/App.tsx
class Expression extends ExpressionBase { }
全文
src/App.tsx
import React from "react";
type Foo = {
bar: string;
}
enum Operator {
plus = "+",
minus = "-",
times = "×",
divide = "÷",
}
interface IExpression {
n: number;
x: number;
o: Operator;
y: number;
Caluculate(): number;
}
abstract class ExpressionBase implements IExpression {
private _n: number = 0;
get n(): number { return this._n; }
set n(value: number) { this._n = value; }
private _x: number = 0;
get x(): number { return this._x; }
set x(value: number) { this._x = value; }
private _o: Operator = Operator.plus;
get o(): Operator { return this._o; }
set o(value: Operator) { this._o = value; }
private _y: number = 0;
get y(): number { return this._y; }
set y(value: number) { this._y = value; }
public constructor(n: number, x: number, o: Operator, y: number) {
this._n = n;
this._x = x;
this._o = o;
this._y = y;
}
public Caluculate(): number {
switch (this.o) {
case Operator.plus: return this.x + this.y; break;
case Operator.minus: return this.x - this.y; break;
case Operator.times: return this.x * this.y; break;
case Operator.divide: return this.x / this.y; break;
default: return 0;
}
}
}
class Expression extends ExpressionBase { }
class App extends React.Component<Foo, {}> {
private expressions: Expression[] = [
new Expression(0, 0, Operator.plus, 1),
new Expression(1, 2, Operator.minus, 3),
new Expression(2, 4, Operator.times, 5),
new Expression(3, 6, Operator.divide, 7),
];
public constructor(props: any) {
super(props);
this.state = {
list: this.expressions
};
this.handleXChange = this.handleXChange.bind(this);
this.handleOChange = this.handleOChange.bind(this);
this.handleYChange = this.handleYChange.bind(this);
}
private handleXChange(i: number, e: any): void {
this.expressions[i].x = e.target.value as number;
this.setState({
list: this.expressions
});
}
private handleOChange(i: number, e: any): void {
this.expressions[i].o = e.target.value as Operator;
this.setState({
list: this.expressions
});
}
private handleYChange(i: number, e: any): void {
this.expressions[i].y = e.target.value as number;
this.setState({
list: this.expressions
});
}
public render(): JSX.Element[] {
return this.expressions.map((d, i) =>
<div key={d.n}>
<input type="number" value={d.x} onChange={(e) => this.handleXChange(i, e)} />
<select value={d.o} onChange={(e) => this.handleOChange(i, e)}>
<option value={Operator.plus}>{Operator.plus}</option>
<option value={Operator.minus}>{Operator.minus}</option>
<option value={Operator.times}>{Operator.times}</option>
<option value={Operator.divide}>{Operator.divide}</option>
</select>
<input type="number" value={d.y} onChange={(e) => this.handleYChange(i, e)} />
<span>=</span>
<input type="number" value={d.Caluculate()} />
</div>
)
}
}
export default App;
動作確認。
感想
- インターフェースは
public
のみ -
getter
はget name()
、setter
はset name()
-
constructor()
は戻り値型指定不可。void
も。 - リテラルでオブジェクトを作ると、メソッドも値も求められた
- 配列の要素の値を更新して
setState()
すると、全てレンダリングされている模様。更新された要素のみレンダリングできないか? - データを外部から渡したい
以上