- Jonas Schmedtmann Udemy NodeJs tutorial code
- Toptal tutorial
- productioncoder youtube and code
- NextJs api middleware example (not used)
- tutorial
-
Hashnode tutorial
-
Tkdodo, React Query Error Handling, ErrorBoundary tutorial
-
React Query reset ErrorBoundary, enable Suspense docs
-
ErrorBoundary, Suspense Render-as-you-fetch example (queryClient.prefetchQuery) Codesandbox
-
SWR Suspense example
-
enable Suspense and ErrorBoundary in React Query -
suspense: true
,useErrorBoundary: true
, thats it -
disable globally ErrorBoundary for a mutation
useErrorBoundary: false
forisError
,error
for local Alert
// lib-client/react-query/queryClientConfig.ts
const queryClientConfig: QueryClientConfig = {
defaultOptions: {
queries: {
suspense: true,
useErrorBoundary: true,
},
mutations: {
useErrorBoundary: false,
},
},
queryCache: new QueryCache({
onError: (error) => console.error('global Query error handler:', error),
}),
mutationCache: new MutationCache({
onError: (error) => console.error('global Mutation error handler:', error),
}),
};
// _app.tsx
const { reset } = useQueryErrorResetBoundary();
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
const fallbackRender = (fallbackProps: FallbackProps) => (
<ErrorFallback {...fallbackProps} fallbackType="screen" />
);
return (
<QueryErrorResetBoundary>
<ErrorBoundary fallbackRender={fallbackRender} onReset={reset}>
<Suspense fallback={<Loading loaderType="screen" />}>
...
</Suspense>
</ErrorBoundary>
</QueryErrorResetBoundary>
);
// test-utils.tsx
// add to wrapper too...
const createTestQueryClient = () =>
new QueryClient({
...queryClientConfig,
defaultOptions: {
...queryClientConfig.defaultOptions,
queries: {
...queryClientConfig.defaultOptions.queries,
retry: false,
},
},
...
});
- useMe overrides default error handler from defaultOptions, React Query default, useHook and mutation options granularity
onError: (error) => {
console.error('me query error: ', error.response);
// id exists but not valid session, clear it
if (id && error.response.status === 404) {
signOut();
}
},
- for
safeParse().error
type"strict": true
is required intsconfig.json
(says in docs)
const result = postsGetSchema.safeParse(req.query);
if (!result.success) throw ApiError.fromZodError(result.error);
// tsconfig.json
"compilerOptions": {
"strict": true, // true required for zod
- solution: pass
await queryClient.prefetchQuery([QueryKeys.ME, me.id], () => me);
in every page - must be in every page separately or entire app will be server side rendered (no static site generation) - Custom App component Next.js docs
const MeProvider: FC<ProviderProps> = ({ children }) => {
// prevent inconsistent state Server:x , Client:y error...
/* Uncaught Error: This Suspense boundary received an update before it finished hydrating.
This caused the boundary to switch to client rendering. The usual way to fix this is
to wrap the original update in startTransition. */
const isMounted = useIsMounted();
const { data } = useMe();
return (
<MeContext.Provider value={{ me: data }}>
{children}
{/* this causes navbar flashing */}
{/* {isMounted ? children : null} */}
</MeContext.Provider>
);
};
if (!data) return null;
in views is because of Typescript"strictNullChecks": true
, because React Query data has typeSomeData | undefined
Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.
- Solution: as error says disabled Prev button in pagination has different values on client and server, null and true (
page === 1
can return null?),page
is state that causes problem when app is still lading/mounting, read error carefully
<Pagination
isPreviousDisabled={!!page && page === 1} // this
- React Github issue answer
- memoize children, seems to work
- possible whenever you change state in
useEffect
and!isMounted
const MeProvider: FC<ProviderProps> = ({ children }) => {
const { data } = useMe();
const memoChildren = useMemo(() => children, [data]);
return (
<MeContext.Provider value={{ me: data ?? null }}>{memoChildren}</MeContext.Provider>
);
};
// additional fix, useMe
// prevent hook to trigger rerender
enabled: isMounted && status !== 'loading',
-
important: only
req.query
are strings ([key: string]: string | string[];
),req.body
preserves correct types (number, boolean), for validation schemas and services argument types -
you can validate id's too with middleware because of
req.query.id
const validateUserCuid = withValidation({
schema: userIdCuidSchema,
type: 'Zod',
mode: 'query',
});
-
non-nullable props stackoverflow
-
env variables types
environment.d.ts
stackoverflow -
env var in Node.js is
string | undefined
, can't be number, must useparseInt(envVar)
-
must be native
MouseEvent
and notReact.MouseEvent
or error inaddEventListener()
// types
const onClick = (event: MouseEvent) => {
const isInsideAnchor =
anchorRef.current !== null && anchorRef.current.contains(event.target as Node);
window.addEventListener('click', onClick);
- make type non-nullable
NonNullable<SomeType>
- ts-node npm readme
# custom cwd
ts-node --cwd <path/to/directory>
- baseUrl to work with ts-node stackoverflow
// yarn add -D tsconfig-paths
// tsconfig.json:
{
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}
- doesn't work with node in production, don't use it, use relative path in server