前回は、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( )する
<TextField
省略
onChange={this.handleChange_address}
省略
/>
constructor(props) {
省略
this.handleChange_address = this.handleChange_address.bind(this);
}
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( ) に取得関数を追加する
interface IState {
省略
updatedTransferTxs: boolean;
}
constructor(props) {
super(props);
this.state={
省略
updatedTransferTxs:false,
}
handleChange_address = (event :React.FormEvent<HTMLInputElement>) => {
this.setState({
省略
updatedTransferTxs:false,
});
};
private transferTxs:TransferTransaction[] = new Array();
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 (
省略
尚、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は配列です。
今回は暗号化メッセージは無視します。(暗号化メッセージあると落ちるかもしれません)
<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>
ソースコード全体
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 新規作成