自己紹介など

やまたつ氏:よろしくお願いします。スライドの中でReact Hooksを使ったコーディングができるようになってるんで、もしよかったらブラウザで開いてみてください。

よろしくお願いします。やまたつっていいます。CureAppっていう会社で働いてます。react, react native, electron, Node.js, MongoDBなどのスタックで医療機器を作ってます。

今日話すのは、React Hooksで遊んでみるって話と、あとdidMountしてみたかったけど、まあやめとけ、っていう話ですね。始めます。

最近タイムラインがすごいReact Hooksで騒がしくなってますね。「React Hooksとは」みたいな話は、これ(スライドが)ドキュメントのリンクになっているの、見るとなんとなくわかると思います。

3行で伝えると、Functional Componentで状態を持てたり、update系のイベントを使えたり、Consumer無しでContextを使えたり、とかとか、っていう感じですね。

もうSFC(注:Stateless Functional Component)っていう言葉は使わなくなるみたいですね。Functionで書いたとしてもStatelessとは限らないので。

それでは始めます。

useState

( デモしながら )

まずuseStateです。

実行してみると(スライド左下のボタンを押す)、描画されるので (右側にボタン出てくる)クリックすると数字が増えていく感じですね ( ボタンをクリックすると数字が増えていく )。

何をしてるかというと、useStateって書いて初期値0を渡しておいて、setCountというやつを使って、incっていうファンクションを作っておいて。あとはjsxを書くだけ、みたいなところですね。まぁ「数字が増えてくぞ」と。

最初はこれだけです。これが、1番ベーシックなHooksになると思います。Functional Componentに状態を持てるぞっていう話ですね。

useEffect

次にuseEffectの話です。

これは(スライドのデモコード上に)怖いことを書いてありますけども、怖いです。

とりあえず見せるのがいいと思うんですけど、1秒ずつ増えてくように作ってあって、useStateで初期値は0で、さっきと同様にincを作って、Effectの中でsetIntervalをして……。これはsetIntervalじゃなくてもいいんですけども。

setIntervalで、incで1000ミリ秒で、こんな感じで増えてくと。ここのReturnが何をしているのかっていうと、付けておいたイベントをリムーブするための関数をここでReturnしておくと、ちゃんと外れます。

これをコメントアウトしてrenderすると、なんか怖い感じに。

最初は普通なんですけども、パチパチってなっていって……。これを長いことやるとあんまりパソコンによくないんで、やめといてください(笑)。

(会場笑)

何が起こってるかというと、リムーブするためのIDを捨てて、crearIntervalされなくなったんで、インターバルが消えないままずっと残っていて。

そのインターバルが次にEffectを動かしたときにまたsetInterbalされて、その2人のsetIntervalがまた1つずつ作って4人になって、4人が8人になって、16人になってという感じで。ねずみ算式にsetIntervalされたのが増えていくから、あんなようなガタガタっていう動きをします。

ちなみにカウントが補足されてるから、1から始まって1、2、3、4、5……、みたいな感じの動きになってるんだと思います、目では追えないけど。こんなことができるuseEffectでした。

ごめんなさい。すごい話を端折ったけど、useEffectのここに関数を渡すと、renderが走りなおしたときに呼ばれると。そんな感じです。

これは変な使い方をしていて。たぶんよくある使い方としては、Reactが出してるサンプルとかを見てみると、こんな感じで書かれてると思います。

Widthを取ってくるようなものを書いて……、動きを見せたほうが早いですね。width減らしたりすると、この数字が動くみたいなやつ。

やっていることは、ウインドウのinnerwidthを取って、setwidthに食べさせるのをuseEffectに書いておくっていう感じですね……。

あ! ごめんなさい。うそうそ。addEventで、resizeでハンドルするみたいな感じ。それで、リムーブのときはremoveEffectするみたいな感じです。ざっくりとした説明ですいません。

感想としては、fire storeのobserveとかって、Returnでremoveくれるじゃないですか。あれだと「けっこうエレガントな感じでobserveを付けれるよな」みたいなことを思ったけど、ねずみ算的になると怖いんで、自分の足を撃たないように書くのがいいかと思います。

useContext

useContextは、サンプルはさっきのincとまったく同じなんですけども、新しいAPIを使ってContextを作って、このuseContextっていうところに食べさせとくと、Contextで使ってたのが使える、みたいな話ですね。

Providerのところは今までと変わらないです。でも今回とかだとuseStateが使えるから、useStateで作ったやつらをContextとして渡しておいて……、みたいなこともできそうですよね。というのでやってみた感じですね。

まとめとしては、「Consumerいらなくなるな」という感じです。

useReducer

次はuseReducerなんですけども、まとめるとreducerっぽく使えるuseStateで、「別にサンプルいらんよ」というような気持ちでした(笑)。たぶんreducerっぽい書き方とか、100万回くらいやってると思うんで。

useCallback

useCallbackは、こんな感じです。

下に「Hi!!」って出てるのが見えますね。これは何をやってるかというと、useCallbackっていうところにファンクションを食べさせて……。memokeyはまだ謎だと思っててください。そしてこれはsayHiっていう関数です。この関数を作ってuseEffectに渡すと、それを呼んでくれるんですけども、このCallbackでくくった関数っていうのは、このコンポーネントがいる限り、そしてmemokeyのshallow-equalが変わらない限りは、ずっと1回しか走らないっていうものです。

何が見せたいかというと、これはめっちゃ増やしていっても、ぜんぜんこの下のHiが増えていかないです。超わかりづらいですよね、すいません。手元にパソコンあったら走らせてみてください。

差分でいうと、ここsayHiっていう関数を作ったんですけども、普通のただの関数でconsole.log(“Hi!!”, count)みたいにやると、何が起こるかというと、……あぁボタンが消えてる。

(修正作業)

これでやると、めっちゃ見づらいと思いますけど、コンソール上で「Hi!! (数字)」ってやつがめっちゃ増えていきます。1回しか走らないっていうuseCallbackの制約から解き放たれているんで、そんな動きをしますね。

ちなみになんですけども、このCallbackに渡した2つ目の引数のmemokeyっていうやつなんですけど、こいつはshallow-equalで変わらない限り、1回しか走らない。例えばこれをなくしてしまったりすると、100万回くらい実行されてしまうので、Callbackでくくった意味はあんまりなくなります。だからuseCallback使うときは、絶対ここにキーを渡さなきゃいけないですね。

例えば1とかにしても、shallow-equalがイコールになってるからいいんですよ。これでも無限に、何回やっても「Hi!!」は1回しか言われない。だけど例えば新しいオブジェクトのインスタンスとかを作っちゃったりすると、「Hi!!」が無限に言われるとか。Reactのよくあるshallow-equalの挙動ですね。

実はこのケースではuseCallbackはいらなくて、useEffectっていう第2引数を受け取れて、同じようにmemokeyを受け取れるんですよ。

これで同じような挙動ができるんで、実はさっきのCallbackとかがいらなかったりして。僕はあんまりどういうケースで使うのかわかってないっていうところがあります。(デモを)見せると、これも同じ動きをします。

まとめると、1回だけ走ってほしいやつですね。

useMemo

useMemoは、これもまとめだけです(笑)。

useCallbackに似てるけど、関数の戻り値だけをくれます。そういう話を聞くとreselectっぽいですよね。Memoizeをしてくれていて、さっきのmemokeyも渡せるので、関数についての条件が変わったら取れる。でもそれを見るとreselectのインターフェースのほうが美しいし、reselectはずっと使う可能性が「微レ存」だなと思います。

useRef

useRef。これも僕はあんまり使わないから説明する気がないっていう、自分勝手なところなんですけども(笑)。

useRefってやって、なんか取れて、これをrefに食べさせるとrefが取れるんですよ。Refを使ったことがある人は、この動きは見たことあるかなと思うんですけども。

こいつのcurrentってやると、そのrefで取れたやつが取れて、それにfocusを走らせることができる。これをonButtonClickに閉じ込めると、focusが取れてる、refが取れてることがわかると思います。focusしてくれると。

これ本当にReactのAPIリファレンスのコピーのままなんで、パクリです、すいません(笑)。

まとめ、「ref取れるね」っていう感じですね。

useImperativeMethods

useImperativeMethods。「Imperative」ってのが「命令」っていう意味らしいんですけども、これはよくわかんなかったんですが、こんなサンプル作ってみました。

左を押すと左に当たって、右を押すと右に当たります。左と右の両方のテキストボックスは、ここにまとめて書いてあって。inputARefとinputBRefっていう、どちらもuseRefで作った安直な名前のやつが当たっているんですけれども。

それをuseImperativeMethodsでくくって、こんな感じに書いて。ここのオブジェクトに、Aのほうにfocusする関数と、Bのほうにfocusする関数を作っておきます。そしてそれをHigher Order Componentsでくくって……。

これ、Reactが提供してくれてるんですよ。でもどう見てもこれHigher Order Componentsに見えるんですけど。

Higher Order Componentsでくくって、FancyInputって名前で出して、これを使ってrefをもう1回useRefで取って……、説明がすごくわかりづらいですよね、本当にすいません。それで、ボタンで押したらfocusが走るようにしておくと、左と右を出し分けられる。だからfocusの統合というか、refの統合ができたり、refのカプセル化みたいなことができるよな、ということを思ってます。

つまり、自作するこのコンポーネントのrefがどういうものであってほしいか、みたいなものをここで表現することができるなあ、という感じですね。

useMutationEffect

次に、useMutationEffectなんですけども、勉強が間に合いませんでした。すいません(笑)。useLayoutEffectも同じく、勉強が間に合わなくて、すいません。

これ、Effectと同じシグネチャーって言ってたんで、同じ使い方をするみたいなんですけど。少なくともuseLayoutEffectっていうのは、レンダリングがされきったあとに使えるみたいなので。useRefと合わせて使ったりすると、レンダリング後のwidthとかが取れたりする……のかな? みたいなところですね。ちょっと勉強不足でした。

didMountがしてみたい

最後になるのですが、didMountっぽいことをしてみたくなるじゃないですか。さっきのuseEffectはdidMountと同じ動きはしないんですよね。 ページの表示でワンタイム、Mountされたタイミングで1回だけfetchしたいとかっていう要件は、絶対あると思うんですけども。

それで誰かが、……この人が誰だかは知らないんですけど(笑)。「俺が最強のやつ考えてみた。」とか言ってたけども。

Dan Abramovが「ちょっと待て」って。「これは結局ファンクションのthisのインスタンスにフィールドを詰めてるのと同じで、ちょっとわかりづらいっしょ」みたいなことを言ってますね。

「お前らの最強のパターン使ったやつで書き換えるのを急がないで、データフェッチングはSuspense使わないと煩わしい感じにしかならんから 」みたいなことが書いてあって。

つまり。

「Supenseを待たれよ!」

終わります。

(会場笑)

(会場拍手)

おまけでいろいろ書いておきました。最近Twitterで流れてきたようなやつです。もしよかったら見てください、以上です。