LoginSignup
0
0

More than 5 years have passed since last update.

TypeScriptでReact 3

Posted at

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.tsxdiffで。

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"
   ]
-}
+}

ブレークすればデバッグ成功。
image.png

参考: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;

動作確認。

image.png

感想

  • インターフェースはpublicのみ
  • getterget name()setterset name()
  • constructor()は戻り値型指定不可。voidも。
  • リテラルでオブジェクトを作ると、メソッドも値も求められた
  • 配列の要素の値を更新してsetState()すると、全てレンダリングされている模様。更新された要素のみレンダリングできないか?
  • データを外部から渡したい

以上

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