⚛️React Recoilでカスタムフックを作ってRecoilを隠匿する
課題
初めてrecoilを使うが、component側でrecoilを使っている事を意識したくない。
カスタムフックの作成
- component側でrecoilをimportしない
- カスタムフックでstateに関する関数を呼び出せるようにする
最小構成
定義側
hooks/userState
import React from 'react';
import { atom, useRecoilState } from 'recoil';
import { User } from '@/types';
const userState = atom<User>({
key: 'userState',
default: null,
});
export const useUser = () => {
const [user, setUser] = useRecoilState(userState);
return {
state: user,
set: setUser,
};
};
component側
import React, { FC } from 'react';
import { useUser } from '@/hooks/userState';
const UserName: FC = () => {
const { state } = useUser();
return <p>{state.name ?? 'No Data'}</p>
}
計算結果を返すgetterの作成
定義側
hooks/userState
import { useCallback} from 'react';
import { atom, useRecoilState } from 'recoil';
import format from 'date-fns/format';
import { User } from '@/types';
const userState = atom<User>({
key: 'userState',
default: null,
});
export const useUser = () => {
const [user, setUser] = useRecoilState(userState);
// 誕生日を指定のフォーマットに変換した文字列を返却する
const getStrBirthday = useCallback(
(strFormat?: string = "YYYY年M月d日") => {
return format(user.birthday, strFormat);
},
[user.birthday]
);
return {
state: user,
set: setUser,
getStrBirthday
};
};
component側
import React, { FC } from 'react';
import { useUser } from '@/hooks/userState';
const UserName: FC = () => {
const { state, getStrBirthday } = useUser();
return <div>
<p>{state.name ?? 'No Data'}</p>
<p>{getStrBirthday()}</p>
</div>
}
Promiseを返すgetterを作成する
定義側
hooks/userState
import { useCallback} from 'react';
import { atom, useRecoilState } from 'recoil';
import { fetchArticlesByUserId } from '@/api'
import { User, Article } from '@/types';
const userState = atom<User>({
key: 'userState',
default: null,
});
export const useUser = () => {
const [user, setUser] = useRecoilState(userState);
// こんな関数が欲しいかどうかは置いといて、例として見てください。てへ
const fetchMyArticles = useCallback(
async () => {
const options = { limit: 10 };
const articles: Article[] = await fetchArticlesByUserId(
user.id,
options
);
return articles;
},
[user.id]
);
return {
state: user,
set: setUser,
fetchMyArticles
};
};
component側
import React, { FC, useState, useEffect } from 'react';
import { useUser } from '@/hooks/userState';
import { Article } from '@/types';
const initialArticles: Article[] = [];
const UserName: FC = () => {
const { state, fetchMyArticles } = useUser();
const [articles, setArticles] = useState(initialArticles)
useEffect(() => {
fetchMyArticles().then((data) => setArticles(data));
}, [fetchMyArticles]);
return <div>
<p>{state.name ?? 'No Data'}</p>
// 表示に関してはテキトーに書きました。
{articles.map((article) => (
<p>{article.body}</p>
))}
</div>
}