Context APIを使ったライブラリを作った話

加藤大志氏:「Context APIを使ったライブラリを作った」というタイトルでお話しします。加藤大志といいます。

フリーランスプログラマーで、日ごろどういうサイクルで生活しているかと言うと、GitHubにコードを書いて、それをネタにブログへアップして、それをネタにTwitterをして、というのをぐるぐるやっています。

あと下にあるイベントは、毎週水曜日にReact/GraphQLアプリ開発塾というのをやっていまして、ほぼ年中無休でやっています。

好みについて話すと、コーディングは好きなんですが、いっぱい書くのはあんまり好きじゃなくて、できるだけ少ない行数で書きたいという感じです。

ECMAScript2015になってアロー関数もでてきたし、今はそれだけに統一して、functionという字もclassという字も書かないようなスタイルでやっています。あんまりメジャーな言葉じゃないですけど、this-less JavaScriptっていう言葉があって。どっちかというと関数型のほうの人が言う言葉なんですけど。

classを使わないというか、そもそもfunctionも関数スコープも使わない、thisも使わないっていう、書き方のシンプルなやつをやろうってなってます。

副作用がないような関数が好みなので、reducerとかReduxは好きなんですけど、小さい関数をテストできるような関数と組み合わせて、ロジックを組み立てるっていうコンセプトも好きなんですけど、最近やっぱり「Redux疲れ」みたいな。「こういうアップデートが多くて大変」みたいな話があって、それは悩みどころかなと思っています。

自分が使うぶんにはいいんですけど、人に説明するときに、簡単そうに見えて実は難しい。プロダクトを作るときはいいけど、それを勉強するという点においては、あんまり優しくないなと思っていて。デファクトっぽいけど小規模開発にはちょっと向いてないのかなというところがありました。

Context APIの登場

Reactの話です。16.3からContext APIというのが入りました。

context自体は昔からあって、Reduxもcontextを使って作られてるわけなんですけど、よくコメントに「ライブラリが使うのはいいけど、それを自分で使うな」みたいなことが書いてある。一応非公式と言われ続けていました。16.3に入って、「ちょっと遅いよ」という感じがしますが、ちゃんと使えるようになりました。

1つおもしろいのが、render propsというのが、テクニックとしては使えることはわかってて、React Nativeなどで使われていたわけですが、公式ドキュメントに載ったのはたぶんこれが初めてなんじゃないかなと思います。「一応render propsを推奨するんだ」とか思っていました。

Context APIが登場したことでReduxの代替になるんじゃないか、みたいな話がしばらく3月以降盛り上がっていました。

私はReact/Reduxのconnectに価値があると思うケース以外は、Reduxを代替できるんじゃないかなと思うんですが、それはそもそもReduxを使う必要がなかったケースだということはよく言われています。

余談ですが、React/ReduxっていうライブラリとReduxというライブラリは、一緒にじゃなくてRedux単体でも使うことができるわけで、connectを使わずにReduxを使うことも一応できるわけです。パフォーマンスはちょっと落ちますが。

Context APIの使い方

Context APIの簡単な使い方です。

これは公式のドキュメントからもってきたような感じですけど、初めにcreateContextをしたものを、Providerで提供して、コンポーネントツリーの下のほうでConsumerというのを使うと、valueが取れるというものです。

細かい説明は省略して、Context APIの簡単な特徴をまとめると、コンポーネントツリーの階層をジャンプして値を渡すことができるというものです。

それで、定義したProvider・Consumerのペア、上でProviderが渡したものを、Consumerでツリーの下のほうで受け取るというものです。

これは一種のグローバル変数みたいなものなので、注意して使わないといけないということになっています。基本的には依存するものが増えてしまって、コンポーネントとしての再利用性が下がるので、気を付けて使うほうがよいということになります。

Context APIができればconnectの代替になるのかというと、そんな簡単ではありません。そもそもContext APIはvalueを渡してくれるだけなので、state管理というようなことはできません。

「それをやるには、こんなふうにやったらいいんじゃないか」みたいな話がまたちらほら出ていて、1番これが素直かな、というのを今サンプルコードで挙げてます。

初めにDEFAULT_STATEを別のテキストで作って、それをProviderでvalueで渡す、valueで渡すとき、……ここでは12行目のところですね、作った値そのものと、それをsetする関数を合わせて渡してあげる、みたいなやり方がいくつか紹介されていたりします。

Contextで作ったものと実際に渡しているもので差分がある。受け取るほうについていえば、表示するほうはシンプルで受け取れますし、Editorと書いてあるところはアップデート関数があるほうもそれを受け取って使えるということです。

そもそもContext APIはProviderがvalueを変更したら対となるConsumerを再レンダリングするということができるようになって、valueが変更さえされなければ何も起こらないので、期待通りのことはできるんですが。これをやるためには、Component.setStateを使わなければいけないことになっています。

作ったライブラリとその特徴

今のテンプレートをそのままライブラリにしようと思って作ったのが、このreact-context-global-stateというもので、単にラップして使えるようになっているだけ。プラスアルファでいくつかあるんですけど。

これを使うと、classの文字を書かないで今やったことができます。

createGlobalStateで、StateProviderとStateConsumerという2つの組を受け取る。これはコンポーネントツリーが浅いのであまり価値がないように見えるかもしれませんが、好きなところでconsumeできるようなかたちになります。

このライブラリを使うと、実際内部でsetStateを使っているので、stateの値を変えると再レンダリングしてくれます。あと1個のcontextですべてのGlobalStateを扱おうとすると、ぜんぜん関係ないコンポーネントもレンダリングされてしまうので、contextを複数作って、1つのstateにするように、任意の数のcontextをばーっと用意して、それを1つの全体でまとめるような仕組みになっています。

TypeScriptの型も今用意していまして、anyもimplicit anyも使わずに全部型付きで使えるようになっています。

すごく短いですがサンプルコードを用意してるので、それで使い方がわかるかなと思います。

Hooksについて

最後におまけですが、きっとみんなHooksの話をするだろうと思って(笑)。

(会場笑)

先ほど自己紹介であったようにclassも書きたくない、functionも書きたくないっていうので、Hooksのことは大歓迎です。

きっと喋りだしたら止まらないのでやめておこうと思います(笑)。世界が変わります。SFCという言葉は言わなくなり、classじゃなきゃいけないってことがめったにないだろうし、今後もどんどんなくなっていくと。

それで、さっき見たrender propsもなくなっちゃうので(笑)、やっと公式で入ったのにやっぱり見にくいって話になりますね。そして、これからcustom Hooksがガンガン登場していくでしょう。

先ほどの話にもあったGraphQLのApolloクライアントを使っているので、今それはrender propsで深くなっているので、あれが早くcustom Hooksできたらいいなと思ってますが、議論がまだ進んでない感じです。

当然出たら作るよねということで、先ほどのGlobalStateのライブラリを同じように使えるreact-hooks-global-stateっていうライブラリも作りました。

すっきり作れましたし、使うほうもすっきり使えるので、今言ったことについて言えばcontextはやっぱりいらないかなって思いました(笑)。

これについては今日お話ししませんが、ブログは書いてあるのでご興味がある方は見てみてください。以上です、ありがとうございました。

(会場拍手)