refactor: 引入react-query, 替代原来的自己实现的reduxHookCacheFactory

pull/13/head
moonrailgun 4 years ago
parent d7e8b305f9
commit 5ec998856b

@ -0,0 +1,13 @@
import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
export const PawProvider: React.FC = React.memo((props) => {
return (
<QueryClientProvider client={queryClient}>
{props.children}
</QueryClientProvider>
);
});
PawProvider.displayName = 'PawProvider';

@ -0,0 +1,17 @@
import { useQuery } from 'react-query';
import { fetchUserInfo, UserBaseInfo } from '../model/user';
function buildCacheFactory<T>(
scope: string,
fetcher: (id: string) => Promise<T>
) {
return (id: string): T | Record<string, never> => {
const { data } = useQuery([scope, id], () => fetcher(id));
return data ?? {};
};
}
export const useUserInfo = buildCacheFactory<UserBaseInfo>(
'user',
fetchUserInfo
);

@ -14,6 +14,7 @@ export type {
export { regField } from './components/FastForm/field';
export { regFormContainer } from './components/FastForm/container';
export type { FastFormContainerComponent } from './components/FastForm/container';
export { PawProvider } from './components/Provider';
// i18n
export { t, setLanguage, useTranslation } from './i18n';
@ -22,6 +23,7 @@ export { t, setLanguage, useTranslation } from './i18n';
export { useAsync } from './hooks/useAsync';
export { useAsyncFn } from './hooks/useAsyncFn';
export { useAsyncRequest } from './hooks/useAsyncRequest';
export { useUserInfo } from './hooks/useCache';
export { useMountedState } from './hooks/useMountedState';
export { useRafState } from './hooks/useRafState';
@ -54,10 +56,6 @@ export {
// redux
export { useAppSelector, useAppDispatch } from './redux/hooks/useAppSelector';
export {
useCachedUserInfo,
useCachedUserInfoList,
} from './redux/hooks/useReduxCache';
export { userActions } from './redux/slices';
export { setupRedux } from './redux/setup';
export { createStore } from './redux/store';

@ -16,6 +16,7 @@
"lodash": "^4.17.21",
"react-i18next": "^11.11.0",
"react-native-storage": "npm:@trpgengine/react-native-storage@^1.0.1",
"react-query": "^3.18.1",
"react-redux": "^7.2.4",
"redux": "^4.1.0",
"str2int": "^1.0.0",

@ -1,141 +0,0 @@
import _get from 'lodash/get';
import _set from 'lodash/set';
import _isNil from 'lodash/isNil';
import { useEffect, useMemo, useRef } from 'react';
import { fetchUserInfo, UserBaseInfo } from '../../model/user';
import { cacheActions } from '../slices';
import type { CacheKey } from '../slices/cache';
import { useAppDispatch, useAppSelector } from './useAppSelector';
// 检查是否需要跳过处理
const isSkipId = (id: string) =>
_isNil(id) ||
id === '' ||
typeof id !== 'string' ||
id.toString().startsWith('_');
interface CacheHookOptions {
forceFetch?: boolean;
}
type GetCacheDataFn = (id: string) => Promise<unknown>;
function reduxHookCacheFactory<T>(
cacheScope: CacheKey,
getCacheData: GetCacheDataFn
) {
const isFetchingDataIdQueue: string[] = []; // 正在请求的id列表
return function useReduxCache(
id: string,
options?: CacheHookOptions
): Partial<T> {
const data = useAppSelector<T>(
(state) => _get(state, ['cache', cacheScope, id]) as any
);
const dispatch = useAppDispatch();
const forceFetchRef = useRef(options?.forceFetch ?? false);
useEffect(() => {
if ((_isNil(data) || forceFetchRef.current === true) && !isSkipId(id)) {
// 如果没有数据或设置了强制重新获取 且 不是内置的UUID
// 从服务端获取缓存信息
if (isFetchingDataIdQueue.indexOf(id) === -1) {
// 没有正在获取缓存信息
console.log(`缓存[${cacheScope}: ${id}]不存在, 自动获取`);
getCacheData(id).then((data) => {
// 从列表中移除
const index = isFetchingDataIdQueue.indexOf(id);
if (index !== -1) {
isFetchingDataIdQueue.splice(index, 1);
}
forceFetchRef.current = false; // 不论怎么样都置为false 表示已经获取过了
dispatch(
cacheActions.setCache({
scope: cacheScope,
id,
data,
})
);
});
isFetchingDataIdQueue.push(id);
}
}
}, [data]);
return data ?? {};
};
}
export const useCachedUserInfo = reduxHookCacheFactory<UserBaseInfo>(
'user',
(userId) => fetchUserInfo(userId)
);
/**
* redux hooks
*
* @param cacheScope
* @param getCacheDispatch dispatch
*/
function reduxHookCacheListFactory<T>(
cacheScope: CacheKey,
getCacheData: GetCacheDataFn
) {
const isFetchingDataIdQueue: string[] = []; // 正在请求的UUID列表
return function hook<R extends Record<string, T> = Record<string, T>>(
idList: string[]
): R {
const cacheList = useAppSelector<R>(
(state) => _get(state, ['cache', cacheScope]) as any
);
const dispatch = useAppDispatch();
const resMap = useMemo<R>(() => {
const map = {} as R;
for (const id of idList) {
if (_isNil(cacheList[id]) && !isSkipId(id)) {
// 如果没有数据则请求数据
// 从服务端获取缓存信息
if (isFetchingDataIdQueue.indexOf(id) === -1) {
// 没有正在获取缓存信息
console.log(`缓存[${cacheScope}: ${id}]不存在, 自动获取`);
getCacheData(id).then((data) => {
// 从列表中移除
const index = isFetchingDataIdQueue.indexOf(id);
if (index !== -1) {
isFetchingDataIdQueue.splice(index, 1);
}
dispatch(
cacheActions.setCache({
scope: cacheScope,
id,
data,
})
);
});
isFetchingDataIdQueue.push(id);
}
continue;
}
// 加入返回的map中
_set(map, id, cacheList[id]);
}
return map;
}, [cacheList, idList.join(',')]);
return resMap;
};
}
export const useCachedUserInfoList = reduxHookCacheListFactory<UserBaseInfo>(
'user',
(userId) => fetchUserInfo(userId)
);

@ -1,32 +0,0 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { UserBaseInfo } from '../../model/user';
import _set from 'lodash/set';
interface CacheState {
user: Record<string, UserBaseInfo>;
}
export type CacheKey = keyof CacheState;
const initialState: CacheState = { user: {} };
const cacheSlice = createSlice({
name: 'cache',
initialState,
reducers: {
setCache(
state,
action: PayloadAction<{
scope: CacheKey;
id: string;
data: unknown;
}>
) {
const { scope, id, data } = action.payload;
_set(state, [scope, id], data);
},
},
});
export const cacheActions = cacheSlice.actions;
export const cacheReducer = cacheSlice.reducer;

@ -1,13 +1,10 @@
import { combineReducers } from '@reduxjs/toolkit';
import { cacheReducer } from './cache';
import { userReducer } from './user';
export const appReducer = combineReducers({
cache: cacheReducer,
user: userReducer,
});
export type AppState = ReturnType<typeof appReducer>;
export { cacheActions } from './cache';
export { userActions } from './user';

@ -1,6 +1,6 @@
import React from 'react';
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';
import { useStorage } from 'pawchat-shared';
import { PawProvider, useStorage } from 'pawchat-shared';
import clsx from 'clsx';
import { Loadable } from './components/Loadable';
@ -13,7 +13,11 @@ const EntryRoute = Loadable(() =>
);
const AppProvider: React.FC = React.memo((props) => {
return <BrowserRouter>{props.children}</BrowserRouter>;
return (
<BrowserRouter>
<PawProvider>{props.children}</PawProvider>
</BrowserRouter>
);
});
AppProvider.displayName = 'AppProvider';

@ -3,7 +3,7 @@ import { Avatar } from './Avatar';
import _isNil from 'lodash/isNil';
import { Skeleton, Space } from 'antd';
// import { openUserProfile } from './modals/UserProfile';
import { useCachedUserInfo } from 'pawchat-shared';
import { useUserInfo } from 'pawchat-shared';
// const UserAvatar = styled(Avatar)`
// cursor: pointer !important;
@ -21,7 +21,7 @@ interface UserListItemProps {
}
export const UserListItem: React.FC<UserListItemProps> = React.memo((props) => {
const { actions = [] } = props;
const userInfo = useCachedUserInfo(props.userId);
const userInfo = useUserInfo(props.userId);
const userName = userInfo.nickname;
const handleClick = useCallback(() => {

@ -314,7 +314,7 @@
core-js-pure "^3.15.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.14.6"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
@ -2097,6 +2097,11 @@ batch@0.6.1:
resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@ -2183,6 +2188,20 @@ braces@^3.0.1, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
broadcast-channel@^3.4.1:
version "3.7.0"
resolved "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
dependencies:
"@babel/runtime" "^7.7.2"
detect-node "^2.1.0"
js-sha3 "0.8.0"
microseconds "0.2.0"
nano-time "1.0.0"
oblivious-set "1.0.0"
rimraf "3.0.2"
unload "2.2.0"
browser-process-hrtime@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
@ -3073,7 +3092,7 @@ detect-newline@^3.0.0:
resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
detect-node@^2.0.4:
detect-node@^2.0.4, detect-node@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
@ -5533,6 +5552,11 @@ joycon@^3.0.1:
resolved "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz#9074c9b08ccf37a6726ff74a18485f85efcaddaf"
integrity sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==
js-sha3@0.8.0:
version "0.8.0"
resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -5973,6 +5997,14 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
match-sorter@^6.0.2:
version "6.3.0"
resolved "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01"
integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==
dependencies:
"@babel/runtime" "^7.12.5"
remove-accents "0.4.2"
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -6050,6 +6082,11 @@ micromatch@^4.0.2, micromatch@^4.0.4:
braces "^3.0.1"
picomatch "^2.2.3"
microseconds@0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
mime-db@1.48.0, "mime-db@>= 1.43.0 < 2":
version "1.48.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d"
@ -6208,6 +6245,13 @@ nan@^2.12.1:
resolved "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
nano-time@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
dependencies:
big-integer "^1.6.16"
nanoclone@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
@ -6471,6 +6515,11 @@ object.values@^1.1.4:
define-properties "^1.1.3"
es-abstract "^1.18.2"
oblivious-set@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
obuf@^1.0.0, obuf@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
@ -7533,6 +7582,15 @@ react-is@^17.0.1:
opencollective "^1.0.3"
opencollective-postinstall "^2.0.2"
react-query@^3.18.1:
version "3.18.1"
resolved "https://registry.npmjs.org/react-query/-/react-query-3.18.1.tgz#893b5475a7b4add099e007105317446f7a2cd310"
integrity sha512-17lv3pQxU9n+cB5acUv0/cxNTjo9q8G+RsedC6Ax4V9D8xEM7Q5xf9xAbCPdEhDrrnzPjTls9fQEABKRSi7OJA==
dependencies:
"@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-redux@^7.2.4:
version "7.2.4"
resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225"
@ -7720,6 +7778,11 @@ relateurl@^0.2.7:
resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
remove-accents@0.4.2:
version "0.4.2"
resolved "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
remove-bom-buffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53"
@ -7898,6 +7961,13 @@ reusify@^1.0.4:
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
@ -7905,13 +7975,6 @@ rimraf@^2.6.3:
dependencies:
glob "^7.1.3"
rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
run-async@^2.2.0:
version "2.4.1"
resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@ -9093,6 +9156,14 @@ universalify@^2.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unload@2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
dependencies:
"@babel/runtime" "^7.6.2"
detect-node "^2.0.4"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"

Loading…
Cancel
Save