Api の呼び出しを hooks で囲うのはイマイチかも
2021-04-02
「Api の呼び出しはすべてグローバルな hooks から呼び出す」というルールが敷かれているプロジェクトをちょくちょく目にしますが。
そのせいでうまいこと実装できないケースに出会ったので、備忘録がてら書いていこうと思います。
まず Api の呼び出しを以下の通りとします。
type GetHogeParams = {
fuga: string;
};
type GetHogeData = {
piyo: number;
};
const getHoge = (params: GetHogeParams) =>
axios.get<GetHogeData>("/api/hoge", {
params,
});
続いて getHoge
を呼び出す hooks を切ります。
const getHogeHooks = (params: GetHogeParams) => {
const [piyo, setPiyo] = useState<GetHogeData["piyo"] | undefined>();
useEffect(() => {
const callback = async () => {
const {
data: { piyo },
} = await getHoge(params);
setPiyo(piyo);
};
callback();
}, [params]);
return piyo;
};
最後に getHogeHooks
を呼び出します。
const Moge: FC = () => {
const piyo = getHogeHooks({ fuga: "fugafuga" });
return typeof piyo === "undefined" ? null : <div>{piyo}</div>;
};
ぱっと見なにも問題なさそうですが、この書き方では例えば以下のようなケースは対応できません。
type MogeProps = {
fuga?: string;
};
const Moge: FC<MogeProps> = ({ fuga }: MogeProps) => {
// props.fuga は undefined を許容しているため渡せない…
const piyo = getHogeHooks({ fuga });
return typeof piyo === "undefined" ? null : <div>{piyo}</div>;
};
そのため、hooks はまだこう書いたほうがマシかなーと思いますが…。
const getHogeHooks = () => {
const callback = useCallback((params: GetHogeParams) => getHoge(params), []);
return callback;
};
個人的な意見として、そもそもグローバルな hooks のみ Api の呼び出しを許容する設計自体が必要ないんじゃないか?と思います。
なので普通にこう書けば良いのでは?とは思います。
type MogeProps = {
fuga?: string;
};
const Moge: FC<MogeProps> = ({ fuga }: MogeProps) => {
const [piyo, setPiyo] = useState<GetHogeData["piyo"] | undefined>();
useEffect(() => {
const callback = async () => {
if (typeof fuga !== "string") {
return;
}
const {
data: { piyo },
} = await getHoge({ fuga });
setPiyo(piyo);
};
callback();
}, [params]);
return typeof piyo === "undefined" ? null : <div>{piyo}</div>;
};
もちろんケースバイケースだとは思いますが、意外とやってしまいそうな実装だったので備忘録がてら。