10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React Hooks 大喜利Advent Calendar 2018

Day 2

23個のサンプルで一貫したこと

Last updated at Posted at 2018-12-01

Hooks に限った話ではないですが、React では最適化をサボるとすぐに不要な再計算や rerender が発生します。GUI設計の話に入る前に、本日は memoize と useContext による最適化について考えます。

memoize が肝心

scroll や touchMove などを利用したGUIの場合は特に気をつけないといけません。下手をすると指が画面に触れている間、包含コンポーネントで rerender しっぱなしという事が起こり得ます。この課題への取り組みとして3つの方法があります。いずれも共通点として、コンポーネントを細分化し rerender 層を薄くする事が重要です。

  • props.children による回避
  • renderProps + useMemo による回避
  • ContextAPI + useMemo による回避

ContextAPI を利用することで Dan が言う complex pattern 解消に近づくので、サンプルでは積極的に ContextAPI を利用しています。

Container 相当の Memoized Component

memoize input array は初見煩わしく思われるかもしれませんし、「将来的にコンパイラが自動で生成してくれる様になる」という一文が公式に注記されています。一方で、この memoize input array が意図的な rerender コントロールを有効にしてくれます。サンプルでよく利用した雛形が以下の様なものです。

  • 「View」 は Hooks さえ許容しない Stateless Function
  • そのひとつ高階層**「Container」**で memoize し props 注入
  • **「View」**の rerender を Container memoize input array でコントロール

Hooks API を利用出来る層を限定する切り口は**「従来SFCは従来のままで良い」**ので個人的に推しです。

Container を構えると、ここで将来 useRedux や useApollo などが合流可能に見えますね。

// ____________________________________
//
// @ View

const View = (props: Props) => (...)
// ____________________________________
//
// @ Container

export default () => {
  const { ... } = useContext(...)
  return useMemo(
    () => (
      <View {...} />
    ),
    [...] // here!!
  )
}

Context Hooks への集約

However, we often can’t break complex components down any further because the logic is stateful and can’t be extracted to a function or another component.

Hooks 利用モチベーションで強調されている一文。Custom Hooks はポータブルで単一責務にするのが良さそうです。

ただし GUI を作るとなると、それらを複合した中粒度の Custom Hooks が欲しくなります。サンプルの中では以下の様に、Custom Hooks 戻り値と Context 定義を対にするシーンが何度もあります。この Context と対の Custom Hooks にビジネスロジックを集約、状態や更新ハンドラを限定的に末端コンポーネントへ公開しています。

import { createContext } from 'react'
import { usePieChart } from './usePieChart'

export const PieChartContext = createContext(
  {} as ReturnType<typeof usePieChart>
)

Custom Hooks の戻り値は自由なので、状態の公開・非公開など、スコープを閉じやすいです。また型との相性も良く、上記の定義だけ初期指定しておけば、実装に推論が追従してくれます。

useReducer を使っていない理由

そもそもこの中粒度で reducer は必要ないという点がひとつ。useReducer は、いずれ来るであろう useRedux が混在した時、混乱の元になると考えているためです。

筆者は Hooks を利用しても、Redux は引き続き使い続けたいと思っています。ContextProvider を中粒度コンポーネントの状態管理 gateway として Store に接続するのが良さそう、と考えています。Redux を「Hooks と Context API で代替しようとすれば可能だが、良いかどうかは別の話」が、今のところの所感です。

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?