フロントエンド初心者がNEM Library使ってメッセージから文字列検索できるようにしてみた(その2)

前回は、TypeScriptでの環境設定とアドレスを入力するとBalanceを出力するサンプルを書きました。
今回は、指定されたアドレスに受信したTransferTransactionの日付とメッセージを一覧で表示します。

1．アドレスを指定される
stateのアドレスがsetState( )で更新され、render( ) がコールされます。

2．受信トランザクションを取得する
render( ) の中に、受信トランザクションの取得処理を入れます。
AccountHttp.imcomingTransaction( )でTransaction[ ]を取得します。
取得したTransaction[ ]からTransferTransactonを抽出します。
Tansaction[ ]の取得とTransferTransactionの抽出が終わったらsetState( )して、
TransferTransactionの抽出が終わった状態へ遷移させます。

3．Material-uiのTableに抽出したTransferTransactonの日付とメッセージを設定する
[Material-uiのTable](https://v0.material-ui.com/#/components/table)を使います。
今回は暗号化メッセージは無視します。(暗号化メッセージあると落ちるかもしれません)


ざっくり説明します。

## 1．アドレスを指定される
[その1](https://qiita.com/kihaku_dol/items/9da5057267c8ed4dec71)参照。

1-1.JSXでのTextFieldのonChangeイベントにハンドラを紐づける
1-2.constructerでハンドルをbind(this)する
1-3.ハンドラ内でsetState( )する

```tsx:1-1.JSXでのTextFieldのonChangeイベントにハンドラを紐づける
          <TextField
                省略
                onChange={this.handleChange_address}
                省略
            />
```

```tsx:1-2.constructerでハンドルをbind(this)する
    constructor(props) {
        省略
        this.handleChange_address = this.handleChange_address.bind(this);
    }
```

```tsx:1-3.ハンドラ内でsetState()する
    handleChange_address = (event :React.FormEvent<HTMLInputElement>, newvalue:string) => {
        this.setState({address:newvalue});
    };
```
state.addressに新しいaddress文字列がセットされ、render( )が呼ばれます。

## 2．受信トランザクションを取得する
受信トランザクションの取得は[AccountHttp.incomingTransactions](https://nemlibrary.com/guide/account/#how-to-receive-incoming-transactions-for-an-account)で行います。

AccountHttpはRxJSのオブジェクトですので、すこしRxJSについても理解しておいたほうがよいです。
以下はRxJSついての私の感想です。
>ざっくりな感想としては、工場のベルトコンベアです。
>川に例えられることが多い気がしますが、私はしっくりきませんでした。
>
>コンベア上をモノが流れていきます。
>流れていくと色々な工程があって、不良品ははじかれたり、ほかのコンベアから流れてきたものと結合させたりして、最後に完成品が出てきます。
>出てきた完成品を使う段階がsubscribeです。
>subscribeはsubscribe(onNext, onError, onComplete)になっています。
>onNextはモノが流れてきたとき、onErrorはコンベアでエラーがあったとき、onCompleteはコンベアが流しきったとき、という感じです。
>[こちら](http://rxmarbles.com/)の図をいろいろと眺めるとなんとなくイメージがつくかもしれません。

AccountHttpのsubscribeはonNextにTransaction[ ]が入ってきます。
当然、Transfer以外のTransactionも含まれているため、抽出が必要になります。
このTransaction[ ]からTransferTransactionだけを抽出します。
nNextは1回しか来ませんが、onCompleteでsetState( )にて、TransferTransactionの更新が終わった状態(state.updatedTransferTxs:true)に遷移させます。
もっとよい使い方があるような気がします。

2-1.stateにTransferTransactionの更新が終わった状態を追加する
2-2.constructorの初期処理にも追加する
2-3.address変更のハンドラ内で、更新前の状態にsetState( )する
2-4.メンバにTransferTransaction[]を持つ
2-5.incomingTransactionの取得関数(更新済みの時は処理しない)
2-6.render( ) に取得関数を追加する

```tsx:2-1.stateにTransferTransactionの更新が終わった状態を追加する
interface IState {
    省略
    updatedTransferTxs: boolean;
}
```

```tsx:2-2.constructorの初期処理にも追加する
    constructor(props) {
        super(props);
        this.state={
            省略
            updatedTransferTxs:false,
        }
```

```tsx:2-3.address変更のハンドラ内で、更新前の状態にsetState()する
    handleChange_address = (event :React.FormEvent<HTMLInputElement>) => {
        this.setState({
            省略
            updatedTransferTxs:false,
        });
    };
```

```tsx:2-4.メンバにTransferTransaction[]を持つ
    private transferTxs:TransferTransaction[] = new Array();
```

```tsx:2-5.incomingTransactionの取得関数(更新済みの時は処理しない)
    private updateIncomingTx() {
        try{
            if(this.state.updatedTransferTxs === false){
                const address = new Address(this.state.address);
                const accountHttp = new AccountHttp();
                accountHttp
                .incomingTransactions(address)
                .map((transactions: Transaction[]): TransferTransaction[] => {
                    return (transactions.filter(x => x.type == TransactionTypes.TRANSFER) as TransferTransaction[])
                })
                .subscribe(
                    (transaction:TransferTransaction[]) => this.transferTxs = transaction,
                    error => console.log(`onError: ${error}`),
                    () => this.setState({updatedTransferTxs:true})
                )
            }
        }catch(e){
            console.log("err : " + e);
        }
    }
```

```tsx:2-6.render()に取得関数を追加する
    render() {
        this.updateIncomingTx();
        return (
               省略
```

尚、onNext内でsetState( )すると、onCompleteより先にrender( )が呼ばれる？っぽくて、
無限に同じTransaction[ ]が流れてきました。
当初、それに気づかず.distinct( ) (同じモノは流れてこないようにするオペレータ)で1回にしていました。

Transaction[ ]からTransferTransactionを抽出し、TransferTransction[ ]をメンバとして保持します。
このTransferTransaction[ ]がTableに入る日付とメッセージのもとになります。

TransactionがTransferTransactionかどうかは、Transaction.typeをTransactionTypes.TRANSFERで判定します。
https://nemlibrary.com/guide/transaction/#how-to-filter-transactions-by-type

キャストが必要になるのですが、TypeScriptの記法としては、`<TransferTransaction[]>`もありますが、
Reactを使っていると、JSXの記法とバッティングするため、` as TransferTransaction[]`の書き方じゃないとキャストできないようでした。

# 3．Material-uiのTableに抽出したTransferTransactonの日付とメッセージを設定する
[Material-uiのTable](https://v0.material-ui.com/#/components/table)を使います。
complex exampleにサンプルコードがあります。
同じやりかたにするために、TransferTransactionは配列です。
今回は暗号化メッセージは無視します。(暗号化メッセージあると落ちるかもしれません)

```tsx:3.Material-uiのTableに抽出したTransferTransactonの日付とメッセージを設定する
 
            <Table>
                <TableHeader>
                    <TableRow>
                        <TableHeaderColumn>TimeStamp</TableHeaderColumn>
                        <TableHeaderColumn>Message</TableHeaderColumn>
                    </TableRow>
                </TableHeader>
                <TableBody >
                    {this.transferTxs.map(
                        (row, index) => (
                            <TableRow key={index}>
                                <TableRowColumn>{row.timeWindow.timeStamp.toString()}</TableRowColumn>
                                <TableRowColumn>{(row.message as PlainMessage).plain()}</TableRowColumn>
                            </TableRow>
                        ))
                    }
                </TableBody>    
            </Table>
```

こんな感じです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/249932/e22c8e26-c33d-abcd-eaf4-fb56863faa02.png)


# ソースコード全体

```tsx:sub-component.tsx
import * as React from 'react';
import {
    AccountHttp, Address, TransferTransaction, TimeWindow, PlainMessage, TransactionTypes, Transaction
} from "nem-library";
import TextField from 'material-ui/TextField';
import {
    Table,TableBody,TableHeader,TableHeaderColumn,TableRow,TableRowColumn
} from 'material-ui/Table';

// Propsの型定義
interface IProps {
  name: string;
}

interface IState {
    address: string;
    updatedTransferTxs: boolean;
}

export class SubComponent extends React.Component<IProps, IState> {
    private transferTxs:TransferTransaction[] = new Array();
    constructor(props) {
        super(props);
        this.state={
            address:"",
            updatedTransferTxs:false,
        }
        
        this.handleChange_address = this.handleChange_address.bind(this);
    }

    handleChange_address = (event :React.FormEvent<HTMLInputElement>) => {
        this.setState({
            address:event.currentTarget.value,
            updatedTransferTxs:false,
        });
    };

    private updateIncomingTx() {
        try{
            if(this.state.updatedTransferTxs === false){
                const address = new Address(this.state.address);
                const accountHttp = new AccountHttp();
                accountHttp
                .incomingTransactions(address)
                .map((transactions: Transaction[]): TransferTransaction[] => {
                    return (transactions.filter(x => x.type == TransactionTypes.TRANSFER) as TransferTransaction[])
                })
                .subscribe(
                    (transaction:TransferTransaction[]) => this.transferTxs = transaction,
                    error => console.log(`onError: ${error}`),
                    () => this.setState({updatedTransferTxs:true})
                )
            }
        }catch(e){
            console.log("err : " + e);
        }
    }

    render() {
        this.updateIncomingTx();
        return (
            <div>
                <h2>{this.props.name}</h2>
                <TextField
                    id="address"
                    value={this.state.address}
                    floatingLabelText="address"
                    onChange={this.handleChange_address}
                    style={{
                        margin: '0 auto',
                        width:`400px`
                    }} 
                />
                <br />
                
                <Table>
                    <TableHeader>
                        <TableRow>
                            <TableHeaderColumn>TimeStamp</TableHeaderColumn>
                            <TableHeaderColumn>Message</TableHeaderColumn>
                        </TableRow>
                    </TableHeader>
                    <TableBody >
                        {this.transferTxs.map(
                            (row, index) => (
                                <TableRow key={index}>
                                    <TableRowColumn>{row.timeWindow.timeStamp.toString()}</TableRowColumn>
                                    <TableRowColumn>{(row.message as PlainMessage).plain()}</TableRowColumn>
                                </TableRow>
                            ))
                        }
                    </TableBody>    
                </Table>
            </div>
        );
    }
}
```

改善点は多々あると思いますので、編集リクエストお待ちしております。

---
今回は、TransferTransactionを取得し、一覧表示は以上です。
・RxJSはベルトコンベア
・subscribeでのsetStateはonNextではなく、onCompleteでする
・React使ってる時のキャストは`<type>`ではなく、`as type`

次回は、検索文字列の入力と、該当Transaction行のみの表示、選択するとトランザクション情報が出力されるようにします。

[フロントエンド初心者がNEM Library使ってメッセージから文字列検索できるようにしてみた(その1)](https://qiita.com/kihaku_dol/items/9da5057267c8ed4dec71)
---
**変更履歴**
2018/05/28 TransferTransactionのフィルタリングをAccontHttpのRxJSに変更
2018/05/25 新規作成