React + Apollo Hooks開発tips
ValueObjectにはerrorの情報も持たせる
DDD的にエラー情報を持たせるのは邪道な気はするが、DOM層でエラー情報を表示したい場合はこっちのほうが便利。
code: typescript
class MailAddress {
readonly value: string;
readonly error?: string;
constractor(value: string) {
this.value = value;
this.error = this.validate(value);
}
validate(password: string) {
if (password.length < 8 ) return 'パスワードは8文字以上入力してください';
...
return;
}
update(password: string) {
return new Password(password);
}
}
バリデーションエラー時にErrorをスローさせる手もあるが、「じゃあどこでcatchする?」となる。
バックエンドならエラーが判明した時点で例外を吐いて、上位層でキャッチ、エラーレスポンスを返すだけで済むが、フロントの場合はそのエラー情報をインタラクティブにユーザーに伝える必要がある。
また、Reactの場合はErrorがスローされると全てのコンポーネントがアンマウントされるので、必ずどこかでErrorはキャッチしないといけない。
欲しいのは「どのようなエラーが発生したのか」をユーザに伝えるためのメッセージであり、「処理を中断させる」機能が欲しいのではない。
Apollo Hooksはそのまま使用せず、薄くラップしたカスタムHooksで提供する
コンポーネント側でApollo Hooksをそのまま使用した場合、キャッシュ周りの取り扱いが露出する。
例えば「キャシュからではなく、必ずネットワーク経由で取得する」など、コンポーネント側からすれば関心のないこと。
この辺は本来インフラ層での仕事だが、Hooksとして提供されているので色んなレイヤーの知識がむき出しになっている。
(ApolloHooksは、インフラ層+リポジトリを内包したもののイメージ)
Apollo Hooksでフェッチしたデータの取り扱い
ページのステート自体はreducerで保持する
apollo hooks でフェッチしたデータは、dispatcher で reducer へ送る
reducer 側では受信したフェッチデータを state へ変換する
イメージ
code:typescript
type Props = { state: State, ... }
function Component: React.FC = ({ state, ... }) => {
return <>{stateの値を用いてレンダリング}</>
}
function Container: React.FC = () => {
const { loading, error } = useXxxxQuery({
onComplete: (data) => dispatch({ type: 'QUERY_TO_STATE', payload: { data } }),
});
if (loading) return <>Loading...</>;
if (error) return <ErrorComponent error={error} />
const props = { state, ... }
return <Component {...props} />
}
query hooks の onCompleteで、取得したデータを dispatcher 経由で reducer に送っている。
dispatcher が presentation とapollo の 橋渡しを担う。
reducer 側にapollo hooks 専用の action を用意することで、今後 apollo hooks から別のライブラリに変わったとしても action の追加・削除だけで済む。