はじめに
年末年始にReactとTypeScriptの旅に出てきました。
行ったことの備忘録として記載します。
理解が浅い部分もあるため、諸々ご容赦いただけると助かります。
Context APIとは
Context APIとはなにか、ないとどうなるかを記載します。
Context APIのない世界
複数間コンポーネントで値の受け渡しをする際、どうしても煩雑になりがちです。
極端な例ですが、App.tsx
から子コンポーネント、更に孫へと受け渡すにつれてコードがバケツリレーと化します。
Context APIのある世界
グローバル(語弊あり)な空間から値を取得、更新することができます。
バケツリレーの必要はなくなりますが、コンポーネントが特定の値に依存してしまうため、再利用性は下がります。
Context APIの使い方
使い方は以下の通りです。
- Contextオブジェクト生成
- Providerの設置
- Contextの利用
Contextオブジェクト生成
interface AppContextType {
users: User[];
setUsers: React.Dispatch<React.SetStateAction<User[]>>;
}
export const AppContext = createContext<AppContextType | undefined>(undefined);
export const AppContextProvider = ({ children }: { children: ReactNode }) => {
const [users, setUsers] = useState<User[]>([]);
return (
<AppContext.Provider value={{ users, setUsers }}>
{children}
</AppContext.Provider>
)
};
AppContext
はcreateContext
で生成したContextオブジェクトです。
AppContextProvider
はProviderの設置で使用します。
Providerの設置
function App() {
return (
<AppContextProvider>
<CssBaseline />
<Router>
<Routes>
<Route path="/" element={<AppLayout />}>
<Route index element={<Home />} />
<Route path="/report" element={<Report />} />
<Route path="/*" element={<NoMatch />} />
</Route>
</Routes>
</Router>
</AppContextProvider>
);
}
先ほど作成したAppContextProvider
をContextを作成したいコンポーネントの上位ツリーに設置します。
つまり、今回の場合には、Home.tsx
やReport.tsx
でAppContext
を読み取れますが、App.tsx
の中では読み取れません。
あくまでAppContextProvider
配下のコンポーネントでだけ読み取れます。
Contextの利用
// useAppContextのカスタムフック
export const useAppContext = () => {
const context = useContext(AppContext);
if (!context) {
// AppContextProvider配下のコンポーネントではない場所での呼び出し
throw new Error("useAppContext must be used within a AppContextProvider");
}
return context;
}
useContext(AppContext)
で呼び出した際にundefined
か判定しなければいけません。
毎度実施するのは面倒ですので、カスタムフックとして記載することで呼び出し側でのコードが簡潔になります。
export default Home = () => {
const { users } = useAppContext();
return (
...
...
)
}
Home.tsx
ではuseAppContext()
を使用するだけでusers
が取り出せました。
動的な値の取得について
users
を元にuser.registration_at
でfilterしたselectedMonthRegistrationUsers
を各所で使用したいとします。
その場合、AppContext.tsx
ではなく、別ファイルにカスタムフックとしてまとめるほうが複雑になりにくくおすすめです。
export default const useSelectedMonthRegistrationUsers = (): User[] => {
const { users, registration_from, registration_to } = useAppContext();
const selectedMonthRegistrationUsers = useMemo(() => {
return users.filter((user) => {
const registrationDate = new Date(user.registration_at);
const fromDate = new Date(registration_from);
const toDate = new Date(registration_to);
return registrationDate >= fromDate && registrationDate <= toDate;
});
}, [users, registration_from, registration_to]);
return selectedMonthRegistrationUsers;
}
なお、カスタムフックはuse~
から始めるのが決まりとのことです。
また、useMemo
を使用することで、第二引数に渡した値([users, registration_from, registration_to]
)が変更された時だけ更新されるようになります。
さいごに
用法用量を守って使わないと、いけない。
銀の弾丸はない。