LoginSignup
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-05-25

前回は、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を使います。
今回は暗号化メッセージは無視します。(暗号化メッセージあると落ちるかもしれません)

ざっくり説明します。

1.アドレスを指定される

その1参照。

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

1-1.JSXでのTextFieldのonChangeイベントにハンドラを紐づける
          <TextField
                省略
                onChange={this.handleChange_address}
                省略
            />
1-2.constructerでハンドルをbind(this)する
    constructor(props) {
        省略
        this.handleChange_address = this.handleChange_address.bind(this);
    }
1-3.ハンドラ内でsetState()する
    handleChange_address = (event :React.FormEvent<HTMLInputElement>, newvalue:string) => {
        this.setState({address:newvalue});
    };

state.addressに新しいaddress文字列がセットされ、render( )が呼ばれます。

2.受信トランザクションを取得する

受信トランザクションの取得はAccountHttp.incomingTransactionsで行います。

AccountHttpはRxJSのオブジェクトですので、すこしRxJSについても理解しておいたほうがよいです。
以下はRxJSついての私の感想です。

ざっくりな感想としては、工場のベルトコンベアです。
川に例えられることが多い気がしますが、私はしっくりきませんでした。

コンベア上をモノが流れていきます。
流れていくと色々な工程があって、不良品ははじかれたり、ほかのコンベアから流れてきたものと結合させたりして、最後に完成品が出てきます。
出てきた完成品を使う段階がsubscribeです。
subscribeはsubscribe(onNext, onError, onComplete)になっています。
onNextはモノが流れてきたとき、onErrorはコンベアでエラーがあったとき、onCompleteはコンベアが流しきったとき、という感じです。
こちらの図をいろいろと眺めるとなんとなくイメージがつくかもしれません。

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( ) に取得関数を追加する

2-1.stateにTransferTransactionの更新が終わった状態を追加する
interface IState {
    省略
    updatedTransferTxs: boolean;
}
2-2.constructorの初期処理にも追加する
    constructor(props) {
        super(props);
        this.state={
            省略
            updatedTransferTxs:false,
        }
2-3.address変更のハンドラ内で、更新前の状態にsetState()する
    handleChange_address = (event :React.FormEvent<HTMLInputElement>) => {
        this.setState({
            省略
            updatedTransferTxs:false,
        });
    };
2-4.メンバにTransferTransaction[]を持つ
    private transferTxs:TransferTransaction[] = new Array();
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);
        }
    }
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を使います。
complex exampleにサンプルコードがあります。
同じやりかたにするために、TransferTransactionは配列です。
今回は暗号化メッセージは無視します。(暗号化メッセージあると落ちるかもしれません)

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

ソースコード全体

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)

変更履歴
2018/05/28 TransferTransactionのフィルタリングをAccontHttpのRxJSに変更
2018/05/25 新規作成

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