大きな目標への道のりは、「帰り道」をイメージする

趣味でよく散歩をするのだが、月一くらいで散歩欲が爆発して5キロくらい歩きたくなることがある。 そういうときは、単純に自宅からゴール地点まで歩くのではなくて、まずゴール地点に電車なりで移動してしまって、そこから自宅まで帰る、というふうにすると楽しく歩けることに気づいた。

これで私は三軒茶屋から経堂まで約4キロを歩いて帰ることに成功しました(参加者の声)。

で、この考え方を応用することで趣味や仕事で長期的な目標を達成できたことが何回かあったので、将来の自分のためにその時考えていたことを記録しておく。

目標を超具体的にイメージする

自宅のイメージが曖昧なやつはいない。だから、帰路が家に近づけば近づくほど安心感が高まるし、「ここまできたらもう大丈夫」という高揚感が背中を押してくれる。 目標を達成したらこんな状態になっていて、そしたらこうしよう、みたいな流れが完璧にイメージできていたほうが、達成することへのモチベーションが高まるのは必然だと思う。イメトレ的な側面でもそうだし、達成した人がその後どうなっているか?を調査することもその後押しになるだろう。

達成した後のことを(一旦)考えない

自宅から遠くまで歩いたとして、「知らない街で、そこからどうする?」を考えるのはストレスになる。いっぽう自宅なら「帰ったらシャワー浴びて寝よう」となるから、帰ることに全エネルギーを注ぎ込むことができる。 目標達成でも同じで、目標に向かっているフェーズではひとまずその達成にだけ心血を注ぎたい。それ以降のことは終わってから考えよう!くらいの覚悟で臨んだ方が、むしろ安定感がある気がする。

目標を動かさない。ダメそうなら手段とスコープを動かす

「家まで帰るのしんどいから、家を動かすか…」とはならない。なるわけない!目標を帰り道だと思えば、それを達成しないという選択肢など存在しない。 もしどうしても帰るのがしんどければ、最寄りの駅まで歩いて電車を使ったり、しぶしぶタクシーを使ったりすることはあるだろうが、家に帰るという目標は変わらない。 目標達成もそんな感じで、できるだけ手段とスコープの変更で達成することを諦めないようにしたい。期限を延ばすことと、達成しないことをなんとしても防ぎたい

近づいていることを実感できる仕組みを作る

帰宅散歩(そう呼んでいます)の醍醐味は、歩けば歩くほど土地勘が増してきて、確実に家に近づいていることが実感できるところにある。逆に、ぼんやり歩いていたら全然違う方面に無駄に歩いていた時のメンタルへのダメージはヤバい。 目標達成の文脈で言い換えれば、定期的に自分の現在位置を計測して成長を実感したりする仕組みがモチベーション維持のためにどうしても必要だということ。それがむずかしければ、「これだけ頑張ったから近づいているはず」と言う自己暗示でも究極良いと思う。「自分は無駄な努力をしているかもしれない」という不安が足を竦ませる。

こんな感じか。ちなみに達成した目標というのは「大喜利大会での優勝」と「エンジニアとしての副業で謎解き業界に関わらせてもらう」の2つ。大喜利大会で優勝したい方は試してみるといいのかも。

パン

ふだん行くパン屋の店員さんは、①お会計する→②パンを袋に入れる→③「お手拭きいくつ入れますか?」→④「レシートがここから出てきますので…」 という流れで動いている。

が、今日の昼にミルクフランスを買いに行ったときの店員さんは、①お会計する→②パンを袋に入れる→③お手拭きを1つ袋に入れる→④「おしぼり…あっ」→⑤(爆笑) という流れだった。その言い間違えそんなに面白いか?と思ったのだが、笑い終わったあと「レシートここから出てきますので」と言いなおしていたので謎が解けた。

おそらく、「お手拭きここから出てきますので」と言おうとしたのを急停止したのだろう。あのおばさんの脳内にはセルフレジの機械からにゅっとお手拭きが出てくる絵面が浮かんでしまったのだろう。それは確かに面白いわ。人の脳内を覗いたような不思議な気持ちになった。

太陽の列車、埼京線

人生で最も愛した街・経堂を離れ、板橋に引っ越した。同棲が始まる。先月末のことだった。オフィス出社のパートナーの都合だから、こればっかりは仕方ない。

 

リモートワークの昼食事情はいっさいの懸念が杞憂だった。スーパーも近いし、なにより板橋はご飯が安い。落とし穴は別のところにあった。

 

埼京線の方面をめちゃくちゃ間違える。

 

新宿に行きたいのに、間違えて赤羽方面の電車に乗ってしまったことが何度あったか。この勘違いを加速させるのが、板橋は1路線駅で、ホームも1つしかないということ。。方向感覚がゼロの自分にとって、左右対称のホームで正しい向きの電車に乗るのはめちゃくちゃ大変なことだ。

 

しかも越してきたばかりで、両側の景色の見分けがあんまりついてない。いざ電車が来ても、小田急みたいに「新宿行き OR 小田原行き」みたいな世界観でもないから、行き先表示だけ見ても上りか下りかわかんなかったりする。新木場ってどっち?

 

そんな終わっている自分の電車事情のなかで、解決策を思いついた。埼京線はほぼ南北に走っているから、新宿方面は真南に向かう。つまり、「新宿にいきたければ、太陽のある方に走っていく電車に乗ればいい」

 

これ。これに気づいてから、板橋から赤羽方面に間違って乗ってしまうことが全くなくなった。アパホテルのある側がこっちで…とか考えなくても、太陽の方に電車が向かっていくイメージなら不思議と間違わなかった。

 

埼京線、太陽の列車tren del sol。板橋に光あれ。

 

 

【今日の知見】react-simple-typewriterで複数行にまたがる文字送りエフェクトを簡単に作る

急にタイプライター風エフェクト(1文字ずつ表示されるやつ)が作りたくなったので、react-simple-typewriterを導入した。

const MyComponent = () => {
  return (
    <div style={{ margin: "1rem" }}>
      <h3>夏目漱石『草枕』より</h3>
      <Typewriter
        words={[
          "智に働けば角が立つ。情に棹させば流される。意地を通せば窮屈だ。とかくに人の世は住みにくい。",
        ]}
      />
    </div>
  );
};

こういうコードを書くだけで、かんたんにゲーム風?通信風?の表示ができる。

カーソルの表示/非表示や表示速度のコントロールも簡単でめちゃくちゃ助かるのだが、改行付きテキストを入れても半角スペースに置換されて複数行表示ができないという問題があってかなり困っていた。しょうがないから自作するか…と思っていたが、ググったらすぐ解決した。

解決策は簡単で、react-simple-typewriterは親要素からスタイルを受け継ぐ。親要素側で改行コードを半角スペースに置換しないスタイルwhite-space:pre-lineを設定してやればよいのだった。 white-space - CSS: カスケーディングスタイルシート | MDN

const MyComponent = () => {
  return (
    <div style={{ margin: "1rem", }}>
      <h3>夏目漱石『草枕』より</h3>
      <Typewriter
        words={[
          "智に働けば角が立つ。情に棹させば流される。\n意地を通せば窮屈だ。とかくに人の世は住みにくい。",
        ]}
      />
    </div>
  );
};

react-simple-typewriter、RPGっぽい挙動をさせたり、ChatGPTっぽい挙動をさせるのにも向いている。簡単に使えて色々設定できる(すべて表示されたときに特定の関数を実行したりできる)ので、存在を知っておくと困った時の助けになってくれそうだ。

1904年になりました(dayjsでの年入力の話)

この記事ははてなエンジニア Advent Calendar 2023 - Hatena Developer Blogの1/1の記事です。


「2024」と打ち込んだはずなのに……

1904年になってしまいました。今年もよろしくお願いします。id:nakatakiです。

辰年生まれなので年男…と言いたいところですが、どうやら年表示が壊れてしまっているようです。この原因はdayjsの仕様の穴にありました。一緒に原因を探してみましょう。

以下のようなコンポーネントがありました。(重要な部分だけをまとめた仮のものです)

const DateInput: React.FC = () => {
  const [selectedDate, setSelectedDate] = useState("");

  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const inputDate = event.target.value;
    const parsedDate = dayjs(inputDate, "YYYY-MM-DD");
    if (parsedDate.isValid()) {
      // 本来は色々処理する
      setSelectedDate(parsedDate.format("YYYY-MM-DD"));
    } else {
      // invalidなら今日の日付にしておく
      setSelectedDate(dayjs().format("YYYY-MM-DD"));
    }
  };

  return (
    <input
      type="date"
      max="9999-12-31"
      value={selectedDate}
      onChange={onChange}
    />
  );
};

これは、dayjsという日時処理ライブラリを利用した、年月日を入力するコンポーネントです。似た機能を持つMoment.jsが新規開発を停止したので、乗り換える…という方も多いと思います。 このような表示になります。

ここでは、様々な事情により、dayjsによるバリデーションを経由したデータをもう一度inputに戻しています。

この状態で一度日付を選択し、キーボードで試しに「1234」と打ち込んでみると…

なんだこれ!

「1901年→1902年→1903年→1904年」と画面が遷移してしまっています。何を入力してもこの調子。 だから「2024」を入力しても「1904」になってしまったんですね。

原因

dayjsのコーナーケース

こうなっている原因は、現在のdayjsのコーナーケースにあります。 こちらをご覧ください。(https://jstool.gitlab.io/dayjs/ を使用しています)

この画像が示すように、現在のdayjsは、'0001-01-01'から'0099-12-31'までの文字列を1900年代として解釈します

JSのDateのコンストラクタで、Yに2桁の数を渡すと1900年台として解釈されるのはそこそこ有名かと思いますが、dayjsの内部でこれを呼び出しているため同様の現象が起きているようでした。

Issueにもなっていますが、現在は仕様として扱われているようです。

Dateとは違って"YYYY-MM-DD" で渡しているんだから1900年代じゃないのは明白じゃないか、とは思うのですが…。

仕組み

仕様がわかればあとは簡単です。先ほどの「1234」の例で、なぜこんな挙動になったか順を追って考えてみます。

  1. キーボードで「1」を押した時点で、inputの中身は'0001-01-01'になっています。
  2. dayjsはこれを1900年1月1日と解釈し、seledtedDate1900-01-01がセットされます。
  3. inputの中身が'1901-01-01'に置き換わってしまうため、フォーカスが下1桁に戻ります。
  4. 次にキーボードで「2」を押すと、inputの中身は'0002-01-01'になります。(2に戻る)

なるほどこれでは、いつまで経っても2024が入力できませんね。

解決

dayjsにStringを渡していてはきりがありません。一旦Date.parseを使ってエポック秒にしてから渡しましょう。

const parsedDate = dayjs(Date.parse(inputDate));

すると…

正常に動作し、無事に2024年になることができました! これで干支も辰年、晴れて年男に…ってあれ?

1904年も辰年だったのか。僕は2000年生まれだから…マイナス96歳って年男になりますか?

まとめ

  • input type="date"の中身をdayjsで逐一パースしていると、キーボード入力がおかしくなるぞ(Moment.jsはこうはならない)
  • dayjsに'0001'年から'0099'年を扱って欲しい時は、StringではなくEpochで渡すと良さそう

TypeScriptでユニオン型をオブジェクトのキーにしたい時はMapped Typesが良さそう(12/27 今日の知見)

今日の知見

TSでEnumを使いたくない時に、それっぽくUnion型を使うことがある。

type Fruits = "りんご"|"みかん"|"バナナ";

そんな時に、こういう制約↓をつけたオブジェクトを定義したいとする。

これならOKだが…

type FruitsPrice = {
  [key in Fruits]: Number 
} // OK

こっちはコンパイルエラーになる。

interface FruitsPrice {
  [key in Fruits]: Number;
} // NG
  • できればinterfaceで書きたいわけだが…今回はそうもいかない!という話

[key in Fruits]の書き方はMapped Typesと呼ばれるもので、現在interfaceには対応できない。( https://typescriptbook.jp/reference/type-reuse/mapped-types)

Mapped Typesは追加のプロパティが書けないなど、安心ではあるけど融通は効かない。Mapped Typesで必ずしも要望が叶うとは限らなそうだ

まとめ

  • 自作のユニオン型をオブジェクトのキーに指定したいときは、Mapped Typesを使うのが望ましい
    • しかし、制約はある
      • interfaceでは使えない
      • 例えば型にないキーを追加したいときは困る
        • …これ本当にどうしたらいいんだろう?解決策あるのかな?
    • だが、情熱はあるみたいに言うな