- pages are just for routing, views are real pages with fetching
await Promise.all([invalidateQueries(...), invalidateQueries(...)])
- avatar and header images in Settings form should be in form state and not React Query state
- keep user and post query cache separated (api calls) on client, ok now
- hook return values trigger rerender, session, status
const { data: session, status } = useSession();
- only replace
getSession
withgetMe
if other props thanuser.id
anduser.email
are accessed, else leavegetSession
, no need for db call - problem:
Error: Rendered more hooks than during the previous render
- different key or subkey in getServerSideProps and useQuery - NO, solution: means you are rendering useQuery useUser hook conditionally and haveif return
before the hook - add Eslint add BOTH plugin and rule react docs
yarn add -D eslint-plugin-react-hooks
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}
- useEffect runs only on browser, for redirect
useEffect(() => {
if (me) router.push(Routes.SITE.HOME);
}, [me, router]);
-
use
enabled
prop in useQuery to wait until async args are not resolved -
<Link />
with custom components requirespassHref
andforwardRef
next docs -
problem:
Loading failed for the <script> with source “https://localhost:3001/_next/static/chunks/pages/settings/%5Busername%5D.js”.
and hard page refresh, solution:
// use this
const settingsHref = `/settings/${user.username}`;
// instead of this
const settingsHref = {
pathname: '/settings/[username]/',
query: { username: user.username },
};
-
QueryKeys array, not a string
// queries
useQuery<ClientUser, AxiosError>([QueryKeys.ME], fn);
// mutations
queryClient.invalidateQueries([QueryKeys.POSTS_DRAFTS]);
- remove setLogger, new logger option
// v3
import { setLogger } from 'react-query';
setLogger(customLogger);
// v4
const queryClient = new QueryClient();
const queryClient = new QueryClient({ logger: customLogger });
-
loading, error and empty state hashnode
-
use
// use await queryClient.refetchQueries([QueryKeys.ME])
to refetch me, no need to pass refetch to context -
problem: refetching ME with undefined
id
, solution:me.id
must be part of the key for fetcher function to use new id arg, Github discussion,useMe
is inMeProvider
it's mounted on every page
// clear keys and cache
queryClient.removeQueries([QueryKeys.ME]);
-
use this for shared cache debugging
const queryData = queryClient.getQueriesData(['posts-home'])
-
problem: shared QueryCache between tests, error 500 tests pass alone and fail in group
-
solution:
-
initial list of handlers passed to setupServer() can not be reset, use
server.use(handler500)
inside the test, it will override success handler discussion -
all constructors (
new QueryCache(), new MutationCache()
) insidenew QueryClient(config)
must be invoked for each test
// lib-client/react-query/queryClientConfig.ts
/**
* important: must be function and not object literal
* so constructors (new QueryCache ...) are invoked for each test
* to prevent shared cache between tests
*/
const getQueryClientConfig = (): QueryClientConfig => ({
defaultOptions: {
queries: {
suspense: true,
useErrorBoundary: true,
},
mutations: {
useErrorBoundary: false,
},
},
// HERE
queryCache: new QueryCache({
onError: (error) => formatError('Query', error),
}),
mutationCache: new MutationCache({
onError: (error) => formatError('Mutation', error),
}),
});
- tests pass even without
--runInBand
jest option
-
useSession and useMe are dependant queries
-
fetch userId only in getServerSideProps and pass it as prop or in cache to avoid async useSession call in useMe
-
React Query providers before SuspenseWrapper???, probably not, only MeProvider
<QueryClientProvider client={queryClient}>
<Hydrate state={dehydratedState}>
<SuspenseWrapper suspenseName="root">
<Component {...pageProps} />
</SuspenseWrapper>
<ReactQueryDevtools />
</Hydrate>
</QueryClientProvider>
-
me.id
must be part of the query key, my discussion -
if it can return undefined must be async for Promise types, interesting
await queryClient.prefetchQuery([QueryKeys.ME_ID], () => me.id);
await queryClient.prefetchQuery([QueryKeys.ME_ID], async () => me?.id);
-
error is in some state update after rerender, even if id in useMe is sync, can use useSession
-
comment out one by one component to isolate it
-
codesndbox example: 1. provider around suspense, 2. provider rerenders because it uses state that updates, 3. child od suspense is lazily imported
-
memoize childern of Suspense, not of the provider??
const Counter2 = lazy(() => import('./Counter2'));
- separate SuspenseWrapper and ErrorBoundaryWrapper, ErrorBoundaryWrapper can go in root
- root SuspenseWrapper in MeProvider or both in PageLayout
- or useMe to use isLoading and not Suspense
- useMe isnt bellow Suspense but Navbar is for screen Loader
- must use
const queryClient = useQueryClient();
to get same client instance and notconst queryClient = new QueryClient();
, that's why it wouldn't work
// NOT THIS
const queryClient = new QueryClient();
// this
const queryClient = useQueryClient();
const handleClick = async () => {
queryClient.invalidateQueries(['me', 1]);
// or
await queryClient.refetchQueries(['me', 1], { exact: true });
};
// RUNTIME check
// throw error to avoid return type Promise<File | null>
export const getImage = async (imageUrl: string | undefined): Promise<File> => {
if (!imageUrl) return Promise.reject(new Error('Invalid imageUrl.'));
//...
// return success File with axios
};
- important: whenever query is not refetching key didnt change, is not unique
- this is optimistic updates after mutation, with objects merging story (swr and apollo) instead of refetch
-
most important cause, drastic:
-
happens when query keys drastically don't match in getServerSideProps
queryClient.prefetchQuery(key, fn)
anduseQuery(key, fn)
on client, page reloads slowly, that's it -
keys need to be managed constant structure... tutorial?, filterkeys() can return different results
-
you can pass different data in SSR and CSR and watch render
-
debug with React Query tools
-
this will help too:
- block all UI until all data is ready, Navbar for example, this
- all props in UserItem available at same time, fixes it, {user, me}, so so, provider should work same as props
- test case repo nemanjam/hydration-test-case
- isLoading in MeProvider and Navbar causes more errors in production