マルチカーソルを使わないVSCodeはただのVSCodeだ!

Misoca+弥生+ALTOA Advent Calendar 2018の10日目のエントリです。

グッと来るタイトルにしようと思った結果、意味不明になってしまったのは自覚している。許してほしい。

※解説編について

何やってるかわからんという声を多数頂いたため、解説編を書いた。
よかったら併せてご覧ください。
マルチカーソルを使わないVSCodeはただのVSCodeだ!〜解説編〜 - memo.md

🤔 マルチカーソル?

さて、VSCodeではカーソルを複数作ることができる。

vscode-doc-jp.github.io

f:id:mugi1:20181207143102g:plain
簡単な動作例

これはVSCodeに限った機能ではなく、SublimeText, Atom, JetBrains製IDEなどでも似たようなことができる。

昔にSublimeTextを使い始めたころから愛用している機能で、私はこれが無いと生きていけない体になっている。

意外と使ってる人いないことに気付く

普段から仕事でもペアプロ・モブプロをすることが多いが、対応しているエディタ・IDEを利用していても、マルチカーソルをゴリゴリ使う人はあまりいないような印象がある。

いつ使えば良いかがわからない?

そういう機能があるのは知ってるし、なんか便利そうなのも知ってるけど、結局どういうときに使えば良いのかが良くわからない、という人がいるかもしれない、と勝手に予想した。

もったいない!

マルチカーソルは非常に強力な機能で、これを使いこなすかどうかでVSCodeの編集速度は劇的に変わると思っている。せっかくVSCodeを使っているのであれば、マルチカーソルもぜひ使いこなして快適なコーディングを楽しんでほしい。

そこで、キー操作などの使い方は調べれば腐るほど出てくるので、「私はこういう時にマルチカーソルを使ってるよ!」というのを紹介したい。

なお、「これがベストな方法だ」ということを言いたいわけでは決して無い。普通に他の機能で代替出来るものもあるし、無駄に思えるものもあるかもしれない。あくまでも参考として受け止めてほしい。

🔥 実例!マルチカーソル

ちなみに私はVim/Emacs両方共のキーバインドを利用していないのでご了承ください。

Vimも一時期使っていたが、キーリピート速度を限界まで高める&マウスを素早く操作するという強引なスタイルのほうが、スマートではないのはわかりつつも自分には合ってた。

検索からの置換

これが一番多く、かつ強力で便利。

f:id:mugi1:20181207143756g:plain
let → const に置き換える例

なお、言うまでもないが普通にエディタやCLIから検索&置換でも同じことは出来る。

検索欄に入力やコピペしなくても良いので、1ステップ分速いのが良くて使うことが多い。

ちなみに、CMD+dを連打して1個ずつ選択しているが、置換対象が適切かどうかの目視での確認も兼ねている。一気に全て選択するショートカットを使うときもある。

JSONで書かれたキーを定数に定義

JSONのフォーマットはわかっていて、それを全部変数に入れたい、みたいなケース。たまにある。

f:id:mugi1:20181207175252g:plain
JSONのキーを一気に抜く

クォートで囲まれた中身だけ抜き出す

たとえば、テストコードからitとして書かれている文字列のみ抜き取りたい、みたいなケースを考えてみてほしい。

愚直に1件ずつやろうとすると結構めんどくさいはず。

f:id:mugi1:20181209173437g:plain
itの名称のみを抜き出す

JSONの整形

何かが一定の規則で羅列されている場合に、それを横並びから一行ずつ、あるいはその逆へ変換するのが非常に強い。

たとえば、JSONの一部だけを横並びで書きたい場合などは便利。

f:id:mugi1:20181207180512g:plain
複数行のJSONを単一行にする。その後複数行に再度戻す。

クエリを書くとき

テーブルの定義は何らかの別の形式で記載されていることが大半だと思う。可能性としては色々ある。

  • schema.rb
  • 社内のナレッジベース
  • テーブル定義書ver2(これが最新です).xlsxという最強の資料

などだろうか。

雑にコピー&ペーストしたところからフィールド名だけぶっこ抜いてクエリにする、みたいなときにササッとやれる。

f:id:mugi1:20181209201950g:plain
DB定義のようなものからカラム名を抜いてクエリを作る

最近だと、大量のIDはわかっていてそれをIN句に放り込んでクエリを投げたい、みたいな時にも便利だった。

f:id:mugi1:20181207174456g:plain
大量のIDをIN句に含むクエリを書く

配列の定義を書く

とにかくある一定のルールで列挙されたものを整形する、という用途に関しては強い。

たとえば、Rubyで%記法での文字列配列を作る場合などに使える。

f:id:mugi1:20181210092250g:plain
Rubyで%記法の文字列をサッと作る

いい感じに全部のケース変換をする

VSCodeはマルチカーソルとは別にケース変換ができる機能がある。 以下のプラグインを入れると、デフォルトに加えてさらに様々な変換も可能になる。

marketplace.visualstudio.com

そしてこれらはマルチカーソルと組み合わせ可能であり、それが非常に強力。

たとえば、APIレスポンスがキャメルケースだが、サーバサイドがRubyで書かれてるのでスネークケースにしたい場合とかがある。

f:id:mugi1:20181209180010g:plain
キャメルケースのレスポンス内容をスネークケースの変数に格納する

あるいは、Vueを使ってるとテンプレート上だけはケバブケースにしたい、みたいなことがある。

f:id:mugi1:20181209181422g:plain
Vueコンポーネントケバブケースでテンプレートに記述する

全行にデバッグログを突っ込む

愚直にデバッグログを仕込みまくる、みたいなことはコード書いてる人なら誰も経験があると思う。(ちなみにもちろんデバッガを使えるなら使ったほうがいい)

各行ごとの実行後の状態を見たい場合などは、出力するログに番号も採番しないと後で判別がつかないが、これがまた面倒くさい。

VSCodeには vscode-input-sequence という素晴らしい拡張機能がある。 marketplace.visualstudio.com

これとマルチカーソルを組み合わせることで一気に連番を振ったログ出力を差し込むことが出来る。

f:id:mugi1:20181209204227g:plain
連番を振ったconsole.logを一気に埋め込む

まとめ

というわけで、事例を元にしたマルチカーソルの使い方を紹介した。

特にここ最近はVSCodeを利用する人が増えてきたと思うけど、マルチカーソルを使わないとかなり勿体無いと思っているので、ぜひ積極的に試してみてほしい。

問題があるとすると、これに慣れるとシェル芸をしなくても大体のことが出来るようになっていくので脳が死んでいく。そして中毒性が高い。

VSCodeで開いてマルチカーソル使ったほうが速いことが多いけど、たまには意識的にターミナルでawkとかsedとか使って操作するようにしたほうがいいとは思う。


冒頭にも書いてありますが、解説編でキー操作を紹介しています。よければあわせてどうぞ。

mugi1.hateblo.jp


Misoca+弥生+ALTOA Advent Calendar 2018、次のエントリは @shinya_kubotaさんによるXamarinの話です!

Nuxt.jsでTypeScriptを使うために色々試して諦めた

最近色々あってあんまりコードが書けてなかったけど、これじゃあかんと思って再開した。

とりあえず、アツいと思ってるものをやろうかな〜ということで、VueFesで話を聞いてからNuxt熱が再燃してきたので、ibuosをNuxtで書き換えてみよう、というのを試していた。

React&MobX&TypeScriptからの置き換え

もともとがReact&MobX&TypeScriptで動いている。これを書き換えたい。

Nuxtを使う以上、

  • React -> Vue
  • MobX -> Vuex

は確定として、TypeScriptをどうするか?という問題が残った。

正直、TypeScript&VSCodeでコードを書く体験を一度味わうと、もうただのPureJSには戻れない感がある。

ということで、NuxtでTypeScriptを使うために色々頑張ってみた。

最終的にはタイトルにある通り諦めたのだが、これを再度構築するの結構ツラいし時間がかかると予想されたので、いつか役に立つかもしれないのでここに残しておく。

型定義を書く

あたりは自分で型定義ファイルを用意した。 @fukuiretuさんの記事やGistをだいぶ参考にさせてもらった。感謝。

最終的にはこんなのが仕上がった。

import Vue from 'vue';
import { Route } from 'vue-router';
import { Store } from 'vuex';
import { AxiosInstance, AxiosRequestConfig, AxiosPromise } from 'axios';

interface NuxtAxiosInstance extends AxiosInstance {
  $request<T = any>(config: AxiosRequestConfig): AxiosPromise<T>;
  $get<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>;
  $delete<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>;
  $head<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>;
  $options<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>;
  $post<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): AxiosPromise<T>;
  $put<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): AxiosPromise<T>;
  $patch<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): AxiosPromise<T>;
}

interface NuxtContext {
  isClient: boolean;
  isServer: boolean;
  isStatic: boolean;
  isDev: boolean;
  isHMR: boolean;
  route: Route;
  store: Store<any>;
  env: object;
  query: object;
  nuxtState: object;
  req: Request;
  res: Response;
  params: { [key: string]: any };
  redirect: (path: string) => void;
  error: (params: { statusCode?: String; message?: String }) => void;
  beforeNuxtRender: (param: { Conmponents: any; nuxtState: any }) => void;
  $axios: NuxtAxiosInstance;
}

declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    layout?: string;
    middleware?: string | String[];
    fetch?: (context: NuxtContext) => void;
    asyncData?: (context: NuxtContext) => void;
    scrollToTop?: boolean;
    transition?: string | object | Function;
    validate?: (context: NuxtContext) => boolean;
    head?: () => { [key: string]: any };
    watchQuery?: string[];
  }
}
declare module '*.vue' {
  import Vue, { ComponentOptions } from 'vue';
  const value: ComponentOptions<Vue>;
  export default value;
}

Vuexで型定義

TypeScript力が足りてないので、正直これであってるのかわからないが、こんな感じになった。 (コードは雰囲気で抜粋してる)

State

export interface State {
  user: User;
}

export const state = (): State => ({
  user: {
    id: null,
    displayName: '',
  }
});

Getter

import { GetterTree, ActionTree, MutationTree } from 'vuex';

export const getters: GetterTree<State, any> = {
  isSignin: (state: State) => state.user.id
};

Action

import { GetterTree, ActionTree, MutationTree } from 'vuex';

export const types = {
  SET_DISPLAY_NAME: 'SET_DISPLAY_NAME',
};

export const actions: ActionTree<State, any> = {
  async updateDisplayName(context, name: string): Promise<void> {
    await (this.$axios as NuxtAxiosInstance).$patch('/myself/name', { name });
    context.commit(types.SET_DISPLAY_NAME, name);
  },
}

Mutation

import { GetterTree, ActionTree, MutationTree } from 'vuex';

export const types = {
  SET_DISPLAY_NAME: 'SET_DISPLAY_NAME',
};

export const mutations: MutationTree<State> = {
  [types.SET_DISPLAY_NAME](state, displayName: string) {
    state.user.displayName = displayName;
  }
}

だいぶ意味不明な感じにはなってる。

  • vuexの XXXTree って使って大丈夫なのか?
  • this.$axios とかを型付けする方法がさっぱりわからない

といった悩みを抱えていたが、とりあえずある程度それっぽくなったのでこれで妥協した。

Vueファイル

たとえば、とある pages/ 配下のファイルからscript部を一部抜粋すると

import Vue from 'vue';
import Component from 'vue-class-component';
import { namespace } from 'vuex-class';
import { State as Auth } from '../store/auth';
import * as auth from '~/store/auth';
import { NuxtContext } from 'types';

const authModule = namespace(auth.name);

@Component({
  head: () => ({ title: 'マイページ' }),
})
export default class MyPage extends Vue {
  @authModule.State user!: auth.User;
  @authModule.Action updateDisplayName: any;

  name = '';

  asyncData(context: NuxtContext) {
    const user: auth.User = context.store.state.auth.user;
    if (!user.id) {
      return context.redirect(
        `/signin?r=${encodeURIComponent(context.route.path)}`
      );
    }
    return { name: user.displayName };
  }

  async handleUpdate() {
    await this.updateDisplayName(this.name);
  }
}

こんなことになった。

Propsなどを受け取るコンポーネント等の場合は、こんな感じ。

import Vue from 'vue';
import Component from 'vue-class-component';
import { Prop } from 'vue-property-decorator';
import { ellipsis } from '~/lib/text';

@Component
export default class EllipsisText extends Vue {
  @Prop({ default: '' })
  text!: string;
  @Prop({ default: 100 })
  size!: number;

  get ellipsisText() {
    return ellipsis(this.text, this.size);
  }
}

いずれにしても、

  • vue-class-component
  • vuex-class
  • vue-property-decorator

あたりを駆使して、うまく型情報を渡していくような書き方になる。

ずいぶん普通にVueを使う場合とは書き方が違うが、 @Props とかは解りやすくて良いな〜と思いながら書いてた。

ESLint & Prettier

正直色々いじりすぎて何のプラグインを入れたか記憶が薄いが、create-nuxt-appを叩いたときに作られる .eslintrc.js をもとに色々いじって、最終的にこうなった。

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  parserOptions: {
    parser: 'typescript-eslint-parser',
  },
  extends: [
    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
    'typescript',
    'plugin:vue/essential',
    'plugin:prettier/recommended'
  ],
  // required to lint *.vue files
  plugins: [
    'vue',
    'prettier'
  ],
  // add your custom rules here
  rules: {
    'typescript/no-var-requires': false,
    'typescript/no-non-null-assertion': false,
    'typescript/explicit-function-return-type': false,
    'typescript/no-angle-bracket-type-assertion': false
  }
}

確かこのあたりを使うのがポイントだったと思う。

  • eslint-plugin-typescript
  • eslint-config-prettier
  • eslint-plugin-prettier
  • typescript-eslint-parser

ビルド時に error TypeError: Cannot set property 'ts' of undefined が出る対策

こちらのissueを参考にして解決。 Nuxt Edge - Issue when running/building the project · Issue #48 · nuxt-community/typescript-template · GitHub

webpackにts-loaderを通してあげる。

front/modules/typescript.js

module.exports = function() {
  // Add .ts & .tsx extension to Nuxt
  this.nuxt.options.extensions.push('ts', 'tsx');

  // Extend webpack build
  this.extendBuild(config => {
    // Add TypeScript
    config.module.rules.push({
      test: /\.tsx?$/,
      loader: 'ts-loader',
      options: { appendTsSuffixTo: [/\.vue$/] },
    });

    // Add .ts extension in webpack resolve
    if (!config.resolve.extensions.includes('.ts')) {
      config.resolve.extensions.push('.ts');
    }

    // Add .tsx extension in webpack resolve
    if (!config.resolve.extensions.includes('.tsx')) {
      config.resolve.extensions.push('.tsx');
    }
  });
};

front/nuxt.config.js

module.exports = {
  ...
  modules: ['~/modules/typescript.js'],
  ...
}

結果

Nuxt.jsで、それなりに型で守られつつ、ESLint/Prettierでコードもきれいに書いていくことが出来るようになった。

正直、いろんなところで躓いて、ここに辿り着くまでに5〜6時間ハマり続けていた。

その後コードを書き進めて...

ここまで頑張ったのに結局かよって感じですが、最終的には、TypeScriptを併用するのはあきらめることにした。

大きい理由は2点ある。

  1. 記述が増えすぎてキツかった

    特にこれはVuex周りで思った。Vuexはサクサク書けるな〜という印象を持ってたんだけど、型で守ろうとすると記述がすさまじく増える感じがあった。

  2. 型付けのための記述のクセが強い

    型を付けようとした際に、クラスコンポーネントやデコレータといったものを駆使して、いかにTypeScriptに型情報を教えてあげるか、といったところがポイントになってくる。これ自体は凄くて、書いてみると「おぉ... Vueに秩序が..!!!」という感動も覚えた。
    ただ、ECMAScriptを使うような、いわゆる良く書くいつものSFCとだいぶ記述の差異が大きい点は考えないといけなくて、しばらく時間をあけてからコードを見たときに、「俺の知ってるVueと違う!」ってなってしまった。(私自身の記憶力とかの問題も大きい。)
    便利ではあるんだけど、標準のレールから結構外れるような感覚を覚えて、これをずっと面倒見ていける自信が沸かなくなってしまった。

とはいえ、これはあくまでも個人の、しかもほぼ誰もアクセスしてこないような小さい趣味サイトの開発するときの一人の感想でしかない。

ある程度の規模であったり、サービス・プロダクトの性質によっては、恩恵がコストを上回ることも十分考えられそうなので、お仕事で使う場合には様々な事情を考慮した上で吟味しないといけないな、と思った。(これはNuxtやTypeScriptに限った話ではないので、常日頃から意識していきたい。)

というわけで、燃え尽きて終了です。

「使っているもの」を管理・共有するWEBサービスを作った

f:id:mugi1:20180830225636p:plain

前々からコツコツ作っていて、形になったので公開した。

ibuos.net

「使っているもの」の管理・共有を主軸においたサービスです。 一人でやってるので荒削りな部分もあるんだけど、コンテンツが揃ってこそ楽しくなってくるので、抵抗がなければ触ってみてもらえると嬉しい。

ちなみに ibuos = 装備(soubi) を逆にしたものです。ダサいね!

できること

  • ログイン(Google or GitHub)
  • 使ってるものを登録 / 一覧
  • ユーザの使ってるものを見る
  • フォローできる(フォロー一覧がまだないので、フォローする意味はまだない!!)
  • 自分の表示名の変更

とりあえず、現状ではまだ機能は少なめです。

作ろうと思った動機

もともと、人の使っているガジェット・ツール・サービスとかに興味があって、社内のesaとかにもそういうまとめがあると積極的に見に行くし、自分も書き込んでいた。

社内だけじゃなくていろんな人のをみたいな〜、と思ったときに、一箇所にまとまってくれてたらいいのに、と思ったのが発端。

あと、単純に物を買うときに、Amazonのレビューとかも見るけど、それ以上に、「あの人が使ってるなら自分も使ってみよう!」っていう動機が多かった。

そういう人は自分以外にも多いんじゃないかと思っていて、そうであれば、眺めてるだけでも楽しめるんじゃないかな〜と思ってる。

技術的な話

フロントエンド

  • React
  • MobX
  • ReactRouter
  • TypeScript

バックエンド

  • Rails(API)
  • firebase
  • Heroku
  • CloudFlare
  • S3

あたりを使ってる

時代はNuxt.jsなのは把握しているけど、TypeScript&MobXを使いたいという欲求だけで突き進んでたら、気がついたらリリースしていた。

最初はSSRを頑張ろうと思ってたけど、まあ個人サービスだし別にええやろ!って思って、途中であきらめた。

もしかしたらいずれNext(or Nuxt)あたりで書き直すかもしれない。

仕事以外で、全てゼロから自分で準備して公開するっていう経験が実は無かっのので、色々と勉強になる部分もあるし、楽しい。 (とはいえ、Herokuとか使ってるのでだいぶチートっぽい感じはある)

なお、PostgreSQLも無料プランなので、ほうっておくとレコード件数上限で死ぬ未来が待っている。幸いにも使ってくれるユーザがいるようであれば、課金したいと思う。

今後の展望

とりあえず、直近では以下を作りたいと思ってる。

  • フォロー一覧・フォロワー一覧
  • ジャンルごとの一覧
  • タグ付け
  • 検索機能

というわけで、ぜひ使ってみてもらえると嬉しい。

そして、

  • エラー出たぞ!
  • こういう機能がほしいぞ!

みたいなのがあれば、@mugi_unoまで教えてもらえると嬉しいです。

React+MobX+TypeScriptが個人的にちょうど良い

Vue使いやすい

以前はフロントエンドといえばReact+Reduxでしょ!というのが主流だったが、最近ではVue+Vuex、あるいはNuxt.jsが注目を集めてる。

私自身も、趣味・業務両方でVueを使っていて、とても使いやすいな〜と思ってる。

TypeScript使うとちょっとつらい

TypeScript自体はかなり強力で、例えばkeyofのような機能はとても魅力的だったりする。

ただ、Vueと組み合わせてTypeScriptを使った型付けをしたい場合に、2018年7月現在ではわりと厳しい感じがしている。私が調べた限りでは、Vue&VSCodeを使うことでthisからの補完などはある程度はできるが、Vuexを絡めた場合には頑張って型を書く必要がある。(Nuxtの場合はもっと大変)

このあたりは、ClassComponentを駆使する有志作成のライブラリを使うことである程度はカバーできる。

ただ、人によって好みもあると思うけど、いわゆる一般的に見られるVueの書き方とは大きく離れることになってしまうことが多く、軽い気持ちで導入するにはハードルが高いな〜、と感じてしまう。

Reactの場合

Reactの場合はTypeScriptとの相性が良く、VSCodeを組み合わせた場合もかなり使いやすい。例えば、JSXに記述するpropsも厳密にチェックすることができる。PropTypesを使わずとも事前に検証できるのも心強い。

状態管理は?

王道だとReduxを採用することになると思うが、私はつらい。(これには個人的なトラウマによる偏見もかなり含まれている。)

これはもちろん開発対象のプロダクトやチームとの相性に依る部分もあるけど、Reduxはかなり記述が増えて、重厚なイメージがある。

もっと軽い感じで使いたい。

このあたりはVuexはとても良くできていて、理解もしやすい。

MobX

mobx.js.org

というわけで、ここ最近MobXを使ってみているが、これはいいな〜という手応えを感じている。

クラスベースでの定義

たとえば、Reduxで一通りのフローを作る場合

  • ActionCreator
  • Reducer
  • Store

あたりを書くことになるが、ファイルがめっちゃ増えたり、Reducerがなんかとんでもない感じに成長してしまうみたいなことがあった。Reducerがとにかくやばい。

MobXの場合は、状態管理をしたいクラスからインスタンスを作成して利用する。こんな感じ。

import axios from 'axios';
import { action, observable, toJS } from 'mobx';

export class Item {
  @observable public name: string = '';
  @observable public image: string = '';

  @action.bound
  public async save(): Promise<void> {
    const res = await axios.post('/items', toJS(this));
    return res.data.id;
  }
}

これが非常にわかりやすく、

  • 状態は @observable 定義のインスタンス変数
  • 処理や状態変更は @action.bound のメソッドを実行

だけで、非常にシンプル。実質ただのインスタンス

そしてTypeScriptと組み合わせた場合に嬉しい効果として、クラス定義されているので、別途型定義を書く必要がない。

Reactと組み合わせる場合にはこれが非常に嬉しく、Propsにクラス定義を指定しておくだけで、そこに紐づくプロパティが全部型チェックできる。

これが出来るかどうかということより、「意識せずに簡単にできる」というのが重要で、書いてて幸せな気持ちになれた。

Reactと組み合わせる

Reactと組み合わせる場合は mobx-react を使う。
このあたりの初期化周りは react-redux を使った場合と似てる。

ただ、ストアにするインスタンスを作って突っ込んでるんだな〜っていう感じで、わりとわかりやすい。

import { Provider } from 'mobx-react';
import { Item } from './Item';

const store = {
  item: new Item()
};

const App = () => (
  <Provider {...store}>
    ...
  </Provider>
);

export default App;

参照する場合には@inject を使う。 react-reduxのときの connectに近い。

import { inject, observer } from 'mobx-react';
import * as React from 'react';
import { Item } from './Item';

interface Props {
  item: Item;
}

@inject('item') @observer
export default class ItemContainer extends React.Component<Props> {
  public render() {
    return (
      <React.Fragment>
        <div>{this.props.item.name}</div>
        <ImageComponent item={this.props.item} />
        <button onClick={() => this.props.item.save()}>save</button>
      </React.Fragment>
    );
  }
}

Actionの呼び出しは、直接インスタンス変数のメソッドを呼び出してる。

この部分。

<button onClick={() => this.props.item.save()}>save</button>

これについては賛否両論あるかもしれない。

Reduxを使っていた場合に、子コンポーネントのReduxへの直接依存を避けるためにメソッドもPropsリレーをしてコードを書いたことがあるが、往々にしてバケツリレー地獄になって、最後には辛いなという気持ちになってた。

結局、「ActionをDispatchするのだけは子コンポーネントからでもOK」みたいになることも多くて、それと同じことをやってる感じ。

ただ、TypeScriptを組み合わせることで、どのメソッドがどのコンポーネントで呼ばれているのかを静的チェックすることができるので、これが非常に強力で、Actionがどこで実行されてるのか把握でき、リファクタリングをする際もある程度ビビらずに直していける。

TypeScriptと使う

というわけで、多々TypeScriptと組み合わせた場合のことを書いた通り、TypeScriptとの相性がかなり良いっぽい。

React+MobXだけで書いた場合には、「簡単に状態管理が出来てうれしい!」くらいで終わりそうだが、TypeScriptを組み合わせることで、クラスベースであることが一気に強力な機能に変わるような印象を受けた。

まとめ

というわけで、React+MobX+TypeScriptがかなり良さそうですよっていうのをどこかに書きたくて書いてみた。

やりたいことを「意識せずに」「簡単に」できるのが大事で、そのあたりを今の所ちょうど良い具合で実現してくれるのがこの組み合わせだな〜と感じてる。

良いことばかり書いてるが、趣味で書いている程度なので、大規模プロダクトの場合や、パフォーマンスが要求される場合などはどうなの?とか色々考えれてない部分はある。

けど、書いてて楽しいので、細かいことはいいやってなってる。

同じことを感じている人がいることに期待している。

とはいえ、Vueはやっぱり使いやすい。pugとかに慣れてると、JSXでclassNameって書くたびにアアアアアア!!!!ってなるし、気をつけててもネストが深くなりがちなのはつらい。

そもそもこれを試したのは「Vue+Vuex (or Nuxt) でTypeScriptを使いたいと思ったけど辛かったから」というのが大きいので、逆に言えばそちらが快適に書けるようになれば、そちらを使うことが多くなりそうではある。

ともあれ、何が一番良いかとかではなく、都度状況を見ながら最適なものを選べるようになっていきたいと思った。

終わり。

Toyama.rbで「オフラインリアルタイムどう書く」をやってみたらとても良かった

Toyama.rb #30のイベントとして、「オフラインリアルタイムどう書く」というのをやった。

toyamarb.connpass.com

とても楽しかったし、参加者の方からも好評だった。

オフラインリアルタイムどう書く

横浜へなちょこプログラミング勉強会さんで実施されているものがオリジナルのイベントです。

趣旨としては、主催者の出した問題を皆で解きましょう!といったもの。

先日のRubyKaigi AfterPartyで@yanchaさんと同じテーブルになる機会があり、そのときに「勉強会のネタに困ってるんですよね〜」という話をしたところ、オリジナルのイベントについて教えていただいた。

Toyama.rbではもくもく会を中心に活動しているが、たまにはテーマを決めてコードを書きたいな〜とも思っていたので、内容的にもピッタリで、とても楽しそうなのでやってみることにした。

当日の流れ

基本的な流れは

  1. 問題を参加者に共有
  2. 同時に、1時間のタイマーを全員が見える形でスタート
  3. 1時間後、トランプを配る
  4. 数の小さい人から発表。解けてなくても、どう挑んだか?みたいなとこを発表する
  5. 「解けた!」という人は最後に発表してもらう

といった感じにした。

2問用意し、だいたい1問につき2時間半程度でやった。

オリジナルである横浜へなちょこプログラミング勉強会さんで開催されている際の流れでは、最後に主催者側が事前に用意していた解答例を最後に解説する形とのこと。

ただ、私自身も楽しみたい!!!という思いから、問題自体は過去問題を利用させていただき、そのかわりに、解けた人に最後に発表してもらう形としてみた。

1時間で誰も解けなかったら少し延長しようかな?と考えていたものの、2問用意していたが、2問ともに1時間できっちり解ける参加者が現れたので、とてもちょうどよかった。

1問目 / フォークじゃない

フォークじゃない 〜 横へな 2014.2.1 問題

1問目は

  • (ほぼ)全員が初挑戦
  • 勝手がよくわからないで戸惑いそう
  • 解ける、あるいは解けそうな感じが見える

のほうが良いかな〜と思っていた。

過去問題には、主にテキストのみで出題されているものと、イメージ・図形が絡む問題のざっくり2種類があった。テキストのみのほうが簡単なのかな?と思い、その中でもスーパーのレジを題材にしている問題がキャッチーな感じだったので利用させていただいた。

結果

1名のみ時間内に完成した。

私はあるバグにハマってしまい、ギリギリ間に合わなかった。その後の発表の最中で気づき、1行だけ追加したら全部通ったのでめっちゃ惜しかった、くやしい。

ちなみに、私が書いたのはこんな感じになった。

2018/7/14 Toyama.rb #30 オフラインリアルタイムどう書く · GitHub

1問目の所感

そもそも「文章の理解にめっちゃ時間が..!!」みたいになってて面白かった。

問題文自体を理解するまでがまず最初の課題だった。

また、コードを書く順番も人によって全然違っていて、一気に作ろうと思うとハマる要素が多かった。

この問題については、

  • 1-9 が来たらレジに並ぶ
  • . が来たらレジが処理をする
  • x が来たらそいつは後ろの客をせき止める

といった形で、段階的に機能追加が可能だったので、そこに気付いてTDD的に作っている参加者がわりと惜しいところ、あるいは正解まで辿り着いていたように見えた。

ただ、どんな言語でもレジに並んでいる人数を算出・計算する部分が少しややこしくなるようで、私も作ってて混乱した。

唯一解けた人は、レジの人数を数値で保持するのではなく、例えば5人なら nnnnn といった形でその分の文字を確保することで、単純に文字列長を見るだけでレジ人数が取れるようになっていた。

1時間という制約の中ではまったく思いつかなかったので、聞いてて素直に「ほ〜〜なるほど〜〜」となっていて、同じ問題でこうも違う解き方になるんだなぁと、聞いてて楽しかった。

あと、Rubyが強い。

Array周りの標準ライブラリの充実度が半端ではないので、配列操作が絡んだときのコードの簡潔さは圧倒的だった。

2問目 / 積み木の水槽

積み木の水槽 〜 横へな 2013.9.6

2問目は、画像が使われてる問題を利用させていただいた。

なんの根拠もないが、画像が絡んでるとそれだけで難しそうに見えたので、「最悪全員解けないのも面白いかな」ということで、この問題にした。

結果

当日は7名の参加者がいて、そのうち1名が中学生プログラマだったのだが、その方のみが鮮やかに解答し、その他普段お金をもらってコードを書いてる大人の皆さん全員が解けないという楽しい結果になった。私自身も1問目とは異なり、もう全然だめだった。

一応、私の書いた無残なコードを残しておく。

2018/7/14 Toyama.rb #30 オフラインリアルタイムどう書く · GitHub

2問目の所感

めっちゃ難しかった!

やってて難しいと思ったポイントは、

  • そもそもどうアプローチしていいかわからない
  • 配列のインデックスがわけわからなくなる

だった。

まず、何をどう考えたらいいのかがわからず、コードを書き始めるまでに15分くらい要してしまった。もうこの時点で私の勝敗は決していた感がある。

また、Webサービスのためにコードを書いている中では、実際のところ二次元配列をX/Y軸を意識しながらゴリゴリに回しながら操作するというのは、そこまで登場頻度は高くはないと思う。

コードを書きながら「あぁ、昔こんな感じにJavaテトリス作ったな..」と懐かしい気持ちになりつつ、完全に混乱していた。

なお、こちらの問題は、1問目よりもさらに他の人の発表を聞くのが楽しかった。

1問目よりも人によってアプローチの差異が大きく、

  • 水が入る場所を1列単位で考えて割り出す人(正解者)
  • 水が入らない死んでいる空間を割り出して、水が残る箇所を逆算しようとする人(私)
  • 全空間に水を入れてから、ライフゲーム的に水を流動させようとする人

と、多種多少だった。最後のライフゲーム的に動かすというのは微塵も思いついてなかったので、すげーなーと思いつつ、実際に完成したものがグラフィカルに描画されるのを見てみたかった。

感想

とても楽しいイベントで、かなり盛り上がった。

2問目で中学生の方が見事に正解コードを書いたあとに、コードの内容に興味津々な大人達が質問しまくって説明してもらって「おー、なるほど!!」となっていて、年齢とか仕事とか関係なくワイワイやれてる感じがあってとても良かった。

そして大人の自分としては「精進せねば...!!!」という気持ちになった。

参加者の方からも「たのしかった!」という声が聞けたので、ぜひともまた開催したいな〜と思う。

その時は、今度はオリジナルの問題を用意したいところだ。


最後に、イベントについてご紹介していただいた@yanchaさん、そして、当日の進め方等について教えていただいた鍋谷さん、ありがとうございました!

リモートワークで昼に冷凍食品食べ続けたのでオススメを紹介する

リモートワークで家で仕事をするようになってから1年以上が経過した。

リモートワーカー昼飯問題

リモートワークをしていると困るのがお昼ご飯。

選択肢としてはいろいろあって、聞いたことがあるものとしては

  • 夕飯の残りを食べる
  • 普通に自分で作る
  • 外で食べる
  • コンビニとかで買う

などがある。

冷凍食品

私は基本的に冷凍食品を食べることが多かった。

栄養が〜!という考えもあると思うけど、個人的には昼食にそもそも栄養バランスなんて期待してないので、他の部分を重要視している。

いろいろメリットがあって

  • 外に出なくていいので楽
  • ほぼ温めるだけでよく、温めてる間に洗濯物を畳んだりできる
  • 安い
  • 最近のものは美味しい

といった具合。

一年も続けると、それなりに種類も食べてきたので、個人的なオススメを紹介したい。


日清具多 辣椒担々麺

https://images-na.ssl-images-amazon.com/images/I/617l3OKqreL.jpg

出典:www.amazon.co.jp

www.nissin.com

これをオススメしたくてこの記事を書いてると言っても過言ではない。

具材のひき肉も結構たくさん入ってて、かなり美味しい。
山椒みたいのが入ってるが、もともとの状態で結構辛いので入れなくてもいいかも。

鍋を用意する必要もなく、電気ケトルとかで300ccのお湯を沸かすだけでいい。

辛いのがだめじゃない人は是非一度買って食べてみてほしい。

セブンイレブンのつけ麺

f:id:mugi1:20180623211549j:plain

チャーシュー盛つけ麺 - セブン-イレブン~近くて便利~

コンビニの冷凍食品もかなりレベルが高くて、最近のはとても美味しい。

特にこのつけ麺が美味しかった。

麺が太麺で結構食べごたえもあるし、つけだれも魚介ベースな感じが美味しい。

これも、つけだれ用のお湯を少し沸かすだけでいいので作るのが楽でいい。

炒飯の極み[えび五目XO醤]

f:id:mugi1:20180623212814p:plain

出典:www.amazon.co.jp

炒飯の極み[えび五目XO醤]|冷凍食品|商品情報|マルハニチロ株式会社

冷凍チャーハンは人によって好みが分かれそうだな〜とは思うけど、個人的にはこれをオススメしたい。

他の冷凍チャーハンはチャーシューだったり、ガツンとした味付けみたいなところで推してるのが多いが、これは珍しくエビがゴロゴロ入ってるタイプ。

(私がエビ好きだっていうのもある)

海鮮感が嫌いじゃない人は食べてもらうといいかも。

ただ、以前Toyama.rbの年末LTでソニックガーデンの木原さんも同じことを言っていた記憶があるが、基本的に冷凍のチャーハンはだいたいうまい。

とりあえずチャーハンがあれば安心感がある。

他のだとこのへんが安定して美味しい。

具材の大きさとか本格っぽさであればコレがかなりすごかった。

が、これは八角の香りが結構強く、人によっては好き嫌いがあるかも。

私は大丈夫だったのでたまに食べてる。

オーマイプレミアム 海の幸のペスカトーレ

f:id:mugi1:20180623220249p:plain

出典:www.amazon.co.jp

www.nippn.co.jp

パスタであればこれがオススメだった。

パッケージを見てもらうとわかるが、具材がすごい。ゴロゴロ入ってる。

そして、これはトレーに入った状態で冷凍されているので、お皿がいらないのもありがたい。洗い物が減る。

味も美味しい。

まとめ

というわけで、

  • 麺(汁あり)
  • つけ麺
  • チャーハン
  • パスタ

の4つを書いてみた。これらは本当によく食べているので、食べたことがなければ是非1回食べてみてほしい。

が、どれだけ美味しかろうとも同じものを食べていると飽きる。

なので、なんかオススメあったら教えてください。(これを言いたかった)

リモートワークしてて気になってきたのでデスク周りのケーブルをスッキリさせた

気がつけばリモートワークを始めてから1年経過していた。

最初にデスク周りは一旦整理したけど、1年もあると色々モノも増えたりしてきて、結構ゴチャゴチャとしてきていた。

特にケーブル周りがひどく、たとえば電源ケーブル等についてはケーブルボックスに収納して見えなくしていたけど、

  • ケーブルボックスから距離があるもの
  • ケーブル自体の長さが中途半端なもの
  • 定期的に抜き差しするためボックスにしまいたくないもの

については適当な状態で、長い間見てみぬフリをしてた。

(以前の状態) f:id:mugi1:20180408151130j:plain

この写真の通り、そのまま垂れ下がってるような感じ。

見えないからいいんじゃね?という考えもできそうだけど、

  • 掃除機をかけるときに邪魔
  • 仕事中に足にケーブルがあたって鬱陶しい
  • なんかイヤ

という問題があるので、気合を入れて整理することにしてみた。

最終的にどうなったか

こうなった

f:id:mugi1:20180408164808j:plain

めっちゃスッキリした。

ポリシー

  • がんばりたくない(コードを1本ずつ固定するとかはやりたくない)
  • ケーブルが増えたり減ったりしても楽に対応したい
  • 雑でいいので、いい感じになってほしい

使ったもの

ネオジム磁石

  • 100均で売ってる

f:id:mugi1:20180408135140j:plain

ステンレスシート

  • ホームセンターに売ってる
  • 1枚700円くらい
  • 買った磁石を持っていって、ちゃんとくっつくものを買う

f:id:mugi1:20180408135058j:plain

ワイヤーネット

  • 100均
  • こちらも磁石がくっつくものを買う
  • ⇣こんな感じのやつ

アイリスオーヤマ メッシュ パネル MPP-6090 ブラック

マスキングテープ

  • 100均
  • なんでもいいけど、磁石より幅があるといい

f:id:mugi1:20180408223200j:plain

普段外さないケーブル類の整理

ワイヤーネットを使って、ケーブルをすべて机の裏に抑えつける。
固定自体は磁石のパワーで雑にやれる。

ステンレスシートを机の裏に貼る

それっぽい場所に適当に貼る

f:id:mugi1:20180408222821j:plain

ワイヤーネットにネオジム磁石を貼り付ける

適当でいい。磁石が強いので勝手にくっつく

f:id:mugi1:20180408154502j:plain

ケーブルをガッと掴んでガッとワイヤーネットに乗せる

ガッとつかんで

f:id:mugi1:20180408222854j:plain

ガッとのせる

f:id:mugi1:20180408222913j:plain

ワイヤーネットごとステンレスシートにガッとくっつける

ネオジム磁石の力によって、適当にやってもいい感じにケーブルごと止まる

f:id:mugi1:20180408222933j:plain

ケーブルが増えたら、ワイヤーネットを引っ張れば簡単に外れるので、 そこにガッとケーブルをねじこんでガッと止めれば終わり。簡単。

取り外すことのあるケーブルの固定

これはお好みでやればいいと思う。

私は磁石が余ったのでやった。

マスキングテープで磁石を挟む

こんなのいくつか作る

f:id:mugi1:20180408223023j:plain

  • マスキングテープ2切れの粘着面で磁石を挟む
    (粘着面同士を貼る。ステンレスシートに磁力でくっつける。)
  • 両端に1個ずつ磁石をいれる
  • どうせ見えないし適当でいい

ステンレスシートにくっつけてケーブルを挟む

こんな感じ。

f:id:mugi1:20180408223047j:plain

簡単に取れるので、ケーブルの取り外しも簡単。

f:id:mugi1:20180408223032j:plain

壁にケーブル止めるやつを作る

これでもまだ磁石が余った。(買いすぎ)

なので、もう何も考えずに、椅子から手がとどく位置の壁にマスキングテープで貼った。

イヤホンとかマイクロUSBケーブルとかがくっついて便利。

f:id:mugi1:20180408231521j:plain

以上です

だいぶスッキリした。

簡単なのでオススメです。