11月27日にReact v16.xの今後のアップデート予定のロードマップが公開されたので、そのことについて触れていきたいと思います。
かなりかいつまんで書いているので、詳細が知りたい方はReact公式ブログを参照してください。
上の記事で書かれている内容は以下の通り
- Suspense for Code Splitting
- React Hooks (~Q1 2019)
- Concurrent Mode (~Q2 2019)
- Suspense for Data Fetching (~mid 2019)
上記に加えて、どのバージョンに含めるかは未定であるものの、2019年中に以下の2つを実装するとのことです。
- Modernizing React DOM
- Suspense for Server Rendering
Suspense for Code Splitting
これはすでにリリースされているv16.6で追加された機能で、React.lazy
とReact.Suspense
を使ったコンポーネントの非同期レンダリングが可能になりました。
詳しくは昨日投稿した記事に書いてあるので、そちらをご覧ください。
React 16.6の新機能、React.lazyとReact.Suspense を使った非同期処理
React Hooks (~Q1 2019)
今までstate
やLifecycle method
などを扱う場合はclassコンポーネントを定義する必要がありましたが、このHooksが導入されると必ずしもclassコンポーネントを使う必要はなくなるようです。
特筆すべきはuseState
とuseEffect
の二つです。
useState
その名の通り関数コンポーネントでステートを使う機能です。
function UseStateSample() {
// countというステートを初期値0で宣言。
const [count, setCount] = useState(0);
return (
<div>
<p>{count} 回クリックしました。</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
);
}
export default UseStateSample;
上のサンプルコードでは関数コンポーネントでuseState
という機能を用いてcountというstateをインクリメントしています。
配列の0番目にstateの変数、1番目にstateの値をセットする関数を定義して、その配列にuseStateで初期化するという書き方です。
useEffect
useEffect
はclassコンポーネントで言うところのcomponentDidMount
とcomponentDidUpdate
に該当する関数コンポーネントならではのライフサイクルメソッドです。
以下のサンプルは、クリックのカウントアップごとのタイトルを変更するというものです。
import { useState, useEffect } from 'react';
function UseEffectSample() {
const [count, setCount] = useState(0);
// マウント時、再描画時に行う処理。
useEffect(() => {
// タイトル変更
document.title = `${count} 回クリックしました`;
});
return (
<div>
<p>You clicked {count} 回クリックしました</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
);
}
export default UseEffectSample;
所感
facebook社はかねてからパフォーマンスを最適化するために可能なかぎり関数コンポーネント(Stateless Function Component)を使うことを推奨していましたが、実装の都合上classコンポーネントを使ってしまいがちになってしまっていました。
この機能がリリースされると、関数コンポーネントを使う機会がかなり増えるので今後のコーディングに大きな影響を及ぼすでしょう。
Concurrent Mode (~Q2 2019)
この機能を使うことによってメインスレッドをブロックすることなくコンポーネントを描画することが可能になります。一言で言えば並列的な描画を実現する機能なのですが、この機能の大きな特徴としては、優先順の高いイベントを時間のかかる描画の処理を中断して割り込ませることができるということです。
// 部分的にConcurrentModeを適用する場合
<React.unstable_ConcurrentMode>
<Something />
</React.unstable_ConcurrentMode>
// 全体にConcurrentModeを適用する場合
ReactDOM.unstable_createRoot(domNode).render(<App />);
Concurrent Mode
の使い方は2通りあって、部分的に使いたい場合はコンポーネントをReact.unstable_ConcurrentMode
でラップするやり方と全体的に適用させる場合はReactDOM.unstable_createRoot
を使うようです。
また、Concurrent ModeではSuspenseにmaxDurationを設定することができ、これによりその値を超える時間内にSuspenseのコンポーネントを表示することが可能な場合にはfallbackの内容を表示させずに、超えた場合にのみ、表示させるといったことが可能になるとのことです。
ただしどちらの接頭辞にもunstableが付いており、まだ仕様の変更はあるかもしれません。
Suspense for Data Fetching (~mid 2019)
v16.6でコード分割のSuspenseは実装されましたが、データ取得時のSuspenseのリリースが来年半ばに予定されています。
// フィックス版のAPIではありません。
import {unstable_createResource as createResource} from 'react-cache';
import {fetch as fetchPolyfill} from 'whatwg-fetch'
// フェッチしたデータをキャッシュする
const Resource = unstable_createResource(fetchData);
function fetchData(id) {
return fetch('/api/data/id').then(res => res.json());
}
function Data(props) {
const data = Resource(props.id);
return <li>{data.title}</li>;
}
function App() {
return (
// データ取得のハンドリングも可能
<React.Suspense fallback={<div>Loading...</div>}>
<ul>
<Data id="1" />
<Data id="2" />
</ul>
</React.Suspense>
);
}
サンプルではreact-cacheを使用していますが、こちらのAPIはまだ不安定のため今後変更などが予定されています。本来Suspenseとは関係ないですが今後セットで使われると思われますので、こちらの動向も確認しておいた方が良いかもしれません。
非同期レンダリングが必要なシチュエーションの多くはAPIのデータフェッチなので、これが使えるようになるとステート管理が随分とスッキリすることになるので、かなり待ち遠しい機能です。
その他のアップデート
Modernizing React DOM
目的としてはバンドルサイズを小さくするのと、ブラウザの挙動に近づけることにあるようです。
こちらはまだ確定された情報はありませんが、reactのリポジトリにあるissueを見るとどういった変更を予定しているかが書かれています。
個人的なところではclassName→classというのは非常にありがたい変更です。地味にめんどくさいので。
Suspense for Server Rendering
読んで字のごとくSuspenseのサーバーサードレンダリング対応です。2019年の主要なアップデートの一つのようですね。
詳細は書かれていないので、今後のアナウンスを待ちましょう。
まとめ
- React Hooksの登場で関数コンポーネントを使うシチュエーションが多くなる。
- そのおかげで親から子へのprops受け渡しのいわゆるラッパー地獄がだいぶマシになる
- ConcurrentModeにより、よりフレンドリーなUXを実現できる。
- データフェッチでSuspenseが使えるとよりステート管理がシンプルになる
- 全体的に今までの記述方法からかなり変更になるので、付いていくのは大変
参考にした記事
React 16.x Roadmap
作って理解するReact Hook
React Conf 2018のKeynoteで発表されたHooks、Concurrent Reactのまとめ