kitak blog

Kみたいなエンジニアになりたいブログ

MobX の runInAction とは

故あって、React + MobX なプログラムを読んでいたら、

runInAction(() => {
  this.state = "done";
});

といった runInAction の呼び出しを多く見かけて、なんだろこれ、と思ったのでメモ。MobX 1日目で変なこと書いているかもしれません。

Writing async actions | MobX を順に読んでいけば分かるのだけど、

  • MobX では strict mode を有効にすることで Action 外で state を変更しようとしたらエラーにすることができる
  • ↑の制約を設けた場合、Action で呼び出した非同期処理のコールバックがただの無名関数だと Action 外ということになるので、コールバックにはメソッドとして定義したアクションを指定する必要がある
  • とはいえ、コールバックのためだけにメソッドを定義するのもまわりくどいので、無名関数を Action にできるユーティリティ関数が用意されている
  • さらに TypeScript で書いている場合に、↑だとコールバックの引数の型を都度記述する必要があって面倒なので、最後の state の変更だけを無名関数に切り出して、アクション内として呼ぶためのユーティリティ関数が用意されている。それが runInAction

ということだった(なんか問題を解決しようとして、さらに問題を生んでを繰り返していて、複雑...)。Action 内の非同期処理のコールバックで state を変更する場合はとりあえず runInAction 経由でおこなえばとりあえずは動く。

Vuex の場合は副作用の伴う処理は Action, state の変更は Mutation で役割が分かれていたのだけど、MobX の Action は副作用の伴う処理も state の変更のいずれも Action 内でおこなうことができる。ただ、「できる」というだけであって、Vuex のように副作用の伴う処理と state の変更で分離する設計をしてもよい。
個人的には副作用が伴う処理とそうでない処理が明確に分かれていたほうが見通しが良いし、書く時に迷わないので、多少は面倒だけども、メソッドをアクションとして定義して、それをコールバックに指定する形で分けると思う。

getUserMedia でバックカメラを要求する

Andoid Chrome で動くQRコード・バーコードを読み取るアプリを書いていて知った。

navigator.mediaDevices.getUserMedia でカメラを要求すると、デフォルトではフロントのカメラになるのだけど、以下のようにすることでバックカメラを要求することができる。

navigator.mediaDevices.getUserMedia({
  video: {
    facingMode: {
      exact: "environment"
    }
  }
})

参考

THE INCAL を読んだ

読んだ。

DUNE で出会ったアレハンドロ・ホドロフスキーメビウスが再びタッグを組んで作ったフレンチSFコミック。
街の一角のありふれた場面からビッグバンのように爆発して、宇宙を覆い尽くさんばかりに物語が膨らんでいく。読んでいて圧倒される作品。 両性具有の話が小道具として出てくるのだけど、その表現がダ・ヴィンチ・コードと繋がるものがあって興味深かった。

アンカル

アンカル

Ethereum トークン (ERC20 Token Standard) の残高を取得する

8月頃にプレセールで買ったとあるトークンが最近発行されて、Etherscan や My Ether Wallet でトークンが発行されたことを確認したのだけど、自分用の Wallet App を作ってみたくなったので、プログラムからトークンの残高を取得する方法を調べてみた。

web3 という Ethereum JavaScript API の module を使う。version は 1.0.0-beta.23。

const Web3 = require('web3');
const web3 = new Web3();
const WALLET_ADDRESS = 'XXX';
const CONTRACT_ADDRESS = 'XXX';

web3.setProvider(new web3.providers.HttpProvider('https://api.myetherapi.com/eth'));
web3.eth.call({
  data: `0x70a08231000000000000000000000000${WALLET_ADDRESS}`,
  to: CONTRACT_ADDRESS
}, "pending").then((res) => {
  console.log(parseInt(res, 16));
})

data の部分は、コントラクトのメソッドを SHA3 256 でエンコードした値(web3.utils.sha3('balanceOf(address)').substring(0,10))とトークンを保管しているアドレスを合わせて、全体が 32 byte になるように結合したもの。

参考

vue-router で初期表示後に、どのルートで解決されたか知りたい

初期ナビゲーションが終わった後のルーターオブジェクトにアクセスすればよい。onReadycurrentRoute のコンボでいける。

router.onReady(() => {
  router.currentRoute;
});

vue-router ひと通りドキュメントに目を通していたつもりだったのですが、ひさしぶりにみたら router オブジェクトのメソッドが色々増えていたり、学びがあった。

Webpack を使っていてファイルの相対パスを書くのがつらくなったとき

小ネタ。

Webpack(というよりモジュールバンドラ) を使っていて、ディレクトリの階層が深くなってくると import や require でロードするファイルのパスを ../../../../foo.js のように ../ の数を正確に指定するゲームになってくる。

以下のように書くことで src ディレクトリをルートにしてパスを指定することができるようになるのだけど

resolve: {
    modules: [
      path.resolve(__dirname, 'src'),
      "node_modules"
    ],
},

同僚氏に npm でいれたパッケージか、src にあるファイルか分からないから、src ディレクトリの alias を定義したらどうか、と勧められた。

resolve: {
    alias: {
        '@': resolve(__dirname, 'src'),
    },
},

これで、どの階層にあるかに関係なく、import store from "@/store" とか import router from "@/router" とか書ける。Vue の Progressive Web App template や Nuxt.js でも同様のことをやっている。最悪、記号の単純な置換か、簡単なスクリプトを書けば済むので、筋は悪くないと思う。

Vue で配列を使った算出プロパティの値が変わらないとき

1回ハマったら体が覚えて、次回から気を付けるようになるのだけど、記事にしておく。

例えば、以下のように配列をスタックとして使い、トップを算出プロパティで定義したとき。1秒後にトップの id が 100 になると思いきやそうはならない。Vue は push, pop, splice といった操作のメソッドをラップして、配列の変更を検知しているので、配列の要素へ代入しても配列の変更とはみなされない。

new Vue({
   data: {
      items: []
   },
   mounted() {
       this.items.push({id: 1});
       this.items.push({id: 2});
       this.items.push({id: 3});
       setTimeout(() => {
           this.items[this.items.length - 1] = {id: 100};
       }, 1000);
   },
   computed: {
      top() {
          if (this.items.length === 0) {
              return null;
          }
          return this.items[this.items.length - 1];
      }
   }
});

これを意図通りに動かすには setTimeout のコールバックの処理を次のように splice メソッドを使って変更するように書き換える。

this.items.splice(this.items.length - 1, 1, {id: 100});