3
0

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 16.3で正式に導入されたContextを使ってcurrent_userを渡すHOCを作ったけど使わなかった

Last updated at Posted at 2018-04-23

モチベーション:バケツリレーやめたい

ご近所SNSマチマチのフロントエンドはReact + Flowtypeで実装されています。ReduxやMobXなどの状態管理のアーキテクチャを採用していないので、current_userなどの様々なコンポーネントが必要とするプロパティは親コンポーネントから子コンポーネント、子から孫、とバケツリレー式に渡しています。

バケツリレー、current_userについて型安全になるので非常にありがたいのですが、当然面倒ではあります。React 16.3でContextが正式に導入されたので、これを使えばcurrent_userのバケツリレーをやめられるかも?と期待に胸を膨らませつつcurrent_userを渡すHigher Order Componentを作ってみました。

Context使ってcurrent_userを渡すHOCを書いた

HOCはこんな感じ

// @flow
import * as React from "react";

type UserObject = {
  id: number,
  name: string
}

// `React.createContext`でContextのインスタンスを作る。
// デフォルト値はNon-nullableであってほしいんだけど、Contextのインスタンスを作る時点でcurrent_userはわからないので仕方ない
const defaultValue: ?UserObject = null;
const UserContext = React.createContext(defaultValue);

// `current_user`を持つオブジェクト型を`current_user`なしにする型
type WithoutCurrentUser<T> = $Diff<
  T & { current_user: UserObject },
  { current_user: UserObject }
>;

// `current_user`をとるコンポーネントをとり、Contextで包んで`current_user`不要のコンポーネントを返すHOC
function withCurrentUser<Props>(
  Component: React.ComponentType<Props>
): React.ComponentType<WithoutCurrentUser<Props>> {
  return props => (
    <UserContext.Consumer>
      {user => {
        if (user) {
          return <Component {...props} current_user={user} />;
        } else {
          throw "userが無かった…";
        }
      }}
    </UserContext.Consumer>
  );
}

export { withCurrentUser };

export default UserContext;

withCurrentUserをこのようにコンポーネントに対して適用して、

import { withCurrentUser } from "./UserContext"; 

class ComponentNeedsCurrentUser extends React.Component<Props, State> {
// ...
}

export withCurrentUser(ComponentNeedsCurrentUser);

このようにUserContext.Providerで包むと、コンポーネントにcurrent_userが渡ります。

<UserContext.Provider value={current_user}>
  <ComponentNeedsCurrentUser
    foo={'foo'}
  />
</UserContext.Provider> 

お気づきかと思いますが、UserContext.Consumerの中で例外を吐いています。これはContextは定義する時点で初期値が必要なのでcurrent_userがNullableになってしまうことへの苦しい対応です。

Nullableになってしまった

バケツリレーをしている間はcurrent_userの渡し忘れは型検査の段階で気づけたのですが、Contextを使うと実行時までわかりません。UserContext.Providerで包み忘れたwithCurrentUserなContextが実行時に例外を吐く、という事態が発生してしまいます。型で検知できたバグが実行時エラーになってしまうのは大きなデメリットです。これは許容できないよねーということで、採用はやめました。

おわりに

HOCの型付けが大変でした。

参考リンクは下記の通りです。公式ドキュメントにHOCの作り方も書いてあって、この記事のHOCもそれを踏襲しています。

ちなみにマチマチではエンジニアを募集しています。これからReact Nativeでアプリを作る予定です。パートタイムも大歓迎なので、興味がある方は是非 @fujimura までご連絡ください。

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?