Open7
RTK Query
RTK Query についてドキュメントや実際に手を使って動かしたりして得た知見をまとめておく
ドキュメントについてのサマリ
- TODO: 読み直してサマリをまとめる https://redux-toolkit.js.org/rtk-query/overview
action について
-
RTK Query がgeenrateするaction nameが存在する
-
ドキュメントにはなさそうなので、そこらへんをまとめておく
-
まとめた内容をメモ
挙動
- 初回のfetchではpending => fulfilledなactionとなり、同一query・cache に対してデータ取得を行うとactionはrejected となる(データはcacheから取得できている)
selector
- selectorはAPI発火が発生しない
- そのため事前にAPIからデータ取得をしていないときに、データ取得が空振るるので注意(これでハマったので)
- 特に、RTK => RTK Query へのリプレイスで単純にselectorへ差し替えていくと発火点が消えてしまうことがあるので。
cache
-
selector でデータ整形をしたいときにどうやるのがベストなのかがわからん
-
transformでデータを変えてしまうほうがよいかもしれない?
transformResponse(res => {
const list = res;
const dict = keyBy(list, "id"); // lodash
return {list, dict}
})
-
ただ、他のselectorと併合したcreateSelectorが出来ない
-
selectFromResultもあることに気づいた
-
transformResponse vs selectFromResult
-
transformResponse = すべてのコンポーネントで利用されるようなコアなデータ
-
selectFromResult = 一部のコンポーネントで利用されるようなデータ = アプリケーションレベルのもの
-
reduxのselectorはstateからのselectだが、RTKQではquery resultからのselectにすること
- つまり、useQuery + selectFromResult
-
useSelectorを使って、stateからのselectも可能ではあるがAPIの発火が行われないので基本的に非推奨
selectFromResult
- 現状の問題として型定義が生成できれないので、selectorを外部で定義するときはややスマートじゃない形になってしまう。
RTKでのselector
// selector.ts
const selectCurrentUser = createSelector(
selectCurrentUserId,
selectUserDict,
(id, dict) => dict[id]
);
// component.ts
const Component = () => {
const user = useSelector(selectCurrentUser);
return <div>{user?.name}</div>
}
RTK Query でのselector(hooks)
/**
* types.ts
*/
type GetUserQueryResponse = { users: User[], dict: Record<string, User> } // useQuery の返り値
type GetUserApiResponse = { users: User[] }; // API の返り値
/**
* api.ts
*/
export const api = createApi({
endpoints: (builder) => ({
getUsers: builder.query<GetUserQueryResponse>({
// データ整形はtransformResponse にて行っておく
transformResponse: (res: GetUserApiResponse) => {
const { users } = res ;
const dict = keyBy(users, "id") // lodash https://lodash.com/docs/4.17.15#keyBy
return { users, dict };
},
}),
}),
});
export const { useGetUsers } = api;
/**
* selector.ts
*
* redux のstateからデータをselectするのではなくて、
* queryの結果からデータをselectするselectorを作成する
*/
const selectCurrentUserByIdFromData = createSelector(
(data: GetUserQueryResponse) => data?.dict ?? {},
(_: unknown, userId: string) => userId,
(dict, userId) => dict[userId],
);
/**
* hooks.ts
*/
export const useSelectUser = () => {
const userId = useSelector(selectCurrenctUserId);
const { currentUser } = useGetUsersQuery(undefined, {
selectFromResult: (result) => {
const currentUser = selectCurrentUserByIdFromData(result.data, userId);
return { ...result, currentUser };
},
});
return currentUser;
};
RTK Query でのselector(without hooks)
// selector.ts
const selectUserDict = (state: RootState) => api.endpoints.getUsers.select(undefined)(state);
const selectCurrentUser = createSelector(
selectCurrentUserId,
selectUserDict,
(id, dict) => dict[id]
);
// component.ts
const Component = () => {
const user = useSelector(selectCurrentUser);
return <div>{user?.name}</div>
}
- 注意点として、APIは呼ばれないので少なくとも1回はどこかで
useGetUsers()
を呼びだしておく必要がある