Skip to content

Commit 29462bb

Browse files
committed
small refactor
1 parent 71c1835 commit 29462bb

File tree

10 files changed

+54
-56
lines changed

10 files changed

+54
-56
lines changed

apps/react-vite/__mocks__/zustand.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { act } from '@testing-library/react';
2+
import { afterEach, vi } from 'vitest';
23
import * as zustand from 'zustand';
34

45
const { create: actualCreate, createStore: actualCreateStore } =
@@ -18,8 +19,6 @@ const createUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
1819

1920
// when creating a store, we get its initial state, create a reset function and add it in the set
2021
export const create = (<T>(stateCreator: zustand.StateCreator<T>) => {
21-
console.log('zustand create mock');
22-
2322
// to support curried version of create
2423
return typeof stateCreator === 'function'
2524
? createUncurried(stateCreator)
@@ -37,8 +36,6 @@ const createStoreUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
3736

3837
// when creating a store, we get its initial state, create a reset function and add it in the set
3938
export const createStore = (<T>(stateCreator: zustand.StateCreator<T>) => {
40-
console.log('zustand createStore mock');
41-
4239
// to support curried version of createStore
4340
return typeof stateCreator === 'function'
4441
? createStoreUncurried(stateCreator)

apps/react-vite/src/app/index.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
1-
import { useQueryClient } from '@tanstack/react-query';
2-
import { useMemo } from 'react';
3-
import { RouterProvider } from 'react-router-dom';
1+
import { AppProvider } from './provider';
2+
import { AppRouter } from './router';
43

5-
import { AppProvider } from './main-provider';
6-
import { createRouter } from './routes';
7-
8-
const AppRouter = () => {
9-
const queryClient = useQueryClient();
10-
11-
const router = useMemo(() => createRouter(queryClient), [queryClient]);
12-
13-
return <RouterProvider router={router} />;
14-
};
15-
16-
function App() {
4+
export const App = () => {
175
return (
186
<AppProvider>
197
<AppRouter />
208
</AppProvider>
219
);
22-
}
23-
24-
export default App;
10+
};

apps/react-vite/src/app/main-provider.tsx renamed to apps/react-vite/src/app/provider.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { QueryClientProvider } from '@tanstack/react-query';
1+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
22
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
33
import * as React from 'react';
44
import { ErrorBoundary } from 'react-error-boundary';
@@ -8,13 +8,20 @@ import { MainErrorFallback } from '@/components/errors/main';
88
import { Notifications } from '@/components/ui/notifications';
99
import { Spinner } from '@/components/ui/spinner';
1010
import { AuthLoader } from '@/lib/auth';
11-
import { queryClient } from '@/lib/react-query';
11+
import { queryConfig } from '@/lib/react-query';
1212

1313
type AppProviderProps = {
1414
children: React.ReactNode;
1515
};
1616

1717
export const AppProvider = ({ children }: AppProviderProps) => {
18+
const [queryClient] = React.useState(
19+
() =>
20+
new QueryClient({
21+
defaultOptions: queryConfig,
22+
}),
23+
);
24+
1825
return (
1926
<React.Suspense
2027
fallback={
Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
1-
import { QueryClient } from '@tanstack/react-query';
2-
import { LoaderFunctionArgs, createBrowserRouter } from 'react-router-dom';
1+
import { QueryClient, useQueryClient } from '@tanstack/react-query';
2+
import { useMemo } from 'react';
3+
import {
4+
LoaderFunctionArgs,
5+
RouterProvider,
6+
createBrowserRouter,
7+
} from 'react-router-dom';
38

49
import { ProtectedRoute } from '@/lib/auth';
510

6-
import { AppRoot } from './app/root';
11+
import { AppRoot } from './routes/app/root';
712

8-
export const createRouter = (queryClient: QueryClient) =>
13+
export const createAppRouter = (queryClient: QueryClient) =>
914
createBrowserRouter([
1015
{
1116
path: '/',
1217
lazy: async () => {
13-
const { LandingRoute } = await import('./landing');
18+
const { LandingRoute } = await import('./routes/landing');
1419
return { Component: LandingRoute };
1520
},
1621
},
1722
{
1823
path: '/auth/register',
1924
lazy: async () => {
20-
const { RegisterRoute } = await import('./auth/register');
25+
const { RegisterRoute } = await import('./routes/auth/register');
2126
return { Component: RegisterRoute };
2227
},
2328
},
2429
{
2530
path: '/auth/login',
2631
lazy: async () => {
27-
const { LoginRoute } = await import('./auth/login');
32+
const { LoginRoute } = await import('./routes/auth/login');
2833
return { Component: LoginRoute };
2934
},
3035
},
@@ -40,13 +45,13 @@ export const createRouter = (queryClient: QueryClient) =>
4045
path: 'discussions',
4146
lazy: async () => {
4247
const { DiscussionsRoute } = await import(
43-
'./app/discussions/discussions'
48+
'./routes/app/discussions/discussions'
4449
);
4550
return { Component: DiscussionsRoute };
4651
},
4752
loader: async () => {
4853
const { discussionsLoader } = await import(
49-
'./app/discussions/discussions'
54+
'./routes/app/discussions/discussions'
5055
);
5156
return discussionsLoader(queryClient)();
5257
},
@@ -55,41 +60,41 @@ export const createRouter = (queryClient: QueryClient) =>
5560
path: 'discussions/:discussionId',
5661
lazy: async () => {
5762
const { DiscussionRoute } = await import(
58-
'./app/discussions/discussion'
63+
'./routes/app/discussions/discussion'
5964
);
6065
return { Component: DiscussionRoute };
6166
},
6267

6368
loader: async (args: LoaderFunctionArgs) => {
6469
const { discussionLoader } = await import(
65-
'./app/discussions/discussion'
70+
'./routes/app/discussions/discussion'
6671
);
6772
return discussionLoader(queryClient)(args);
6873
},
6974
},
7075
{
7176
path: 'users',
7277
lazy: async () => {
73-
const { UsersRoute } = await import('./app/users');
78+
const { UsersRoute } = await import('./routes/app/users');
7479
return { Component: UsersRoute };
7580
},
7681

7782
loader: async () => {
78-
const { usersLoader } = await import('./app/users');
83+
const { usersLoader } = await import('./routes/app/users');
7984
return usersLoader(queryClient)();
8085
},
8186
},
8287
{
8388
path: 'profile',
8489
lazy: async () => {
85-
const { ProfileRoute } = await import('./app/profile');
90+
const { ProfileRoute } = await import('./routes/app/profile');
8691
return { Component: ProfileRoute };
8792
},
8893
},
8994
{
9095
path: '',
9196
lazy: async () => {
92-
const { DashboardRoute } = await import('./app/dashboard');
97+
const { DashboardRoute } = await import('./routes/app/dashboard');
9398
return { Component: DashboardRoute };
9499
},
95100
},
@@ -98,8 +103,16 @@ export const createRouter = (queryClient: QueryClient) =>
98103
{
99104
path: '*',
100105
lazy: async () => {
101-
const { NotFoundRoute } = await import('./not-found');
106+
const { NotFoundRoute } = await import('./routes/not-found');
102107
return { Component: NotFoundRoute };
103108
},
104109
},
105110
]);
111+
112+
export const AppRouter = () => {
113+
const queryClient = useQueryClient();
114+
115+
const router = useMemo(() => createAppRouter(queryClient), [queryClient]);
116+
117+
return <RouterProvider router={router} />;
118+
};

apps/react-vite/src/lib/react-query.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
UseMutationOptions,
3-
DefaultOptions,
4-
QueryClient,
5-
} from '@tanstack/react-query';
1+
import { UseMutationOptions, DefaultOptions } from '@tanstack/react-query';
62

73
export const queryConfig = {
84
queries: {
@@ -13,10 +9,6 @@ export const queryConfig = {
139
},
1410
} satisfies DefaultOptions;
1511

16-
export const queryClient = new QueryClient({
17-
defaultOptions: queryConfig,
18-
});
19-
2012
export type ApiFnReturnType<FnType extends (...args: any) => Promise<any>> =
2113
Awaited<ReturnType<FnType>>;
2214

apps/react-vite/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import { createRoot } from 'react-dom/client';
33

44
import './index.css';
5-
import App from './app';
5+
import { App } from './app';
66
import { enableMocking } from './testing/mocks';
77

88
const root = document.getElementById('root');

apps/react-vite/src/testing/setup-tests.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import '@testing-library/jest-dom/vitest';
22

3-
import { queryClient } from '@/lib/react-query';
43
import { initializeDb, resetDb } from '@/testing/mocks/db';
54
import { server } from '@/testing/mocks/server';
65

@@ -25,5 +24,4 @@ beforeEach(() => {
2524
afterEach(() => {
2625
server.resetHandlers();
2726
resetDb();
28-
queryClient.clear();
2927
});

apps/react-vite/src/testing/test-utils.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import userEvent from '@testing-library/user-event';
77
import Cookies from 'js-cookie';
88
import { RouterProvider, createMemoryRouter } from 'react-router-dom';
99

10-
import { AppProvider } from '@/app/main-provider';
10+
import { AppProvider } from '@/app/provider';
1111

1212
import {
1313
createDiscussion as generateDiscussion,

docs/performance.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Code splitting involves splitting production JavaScript into smaller files to op
66

77
Ideally, code splitting should be implemented at the routes level, ensuring that only essential code is loaded initially, with additional parts fetched lazily as needed. It's important to avoid excessive code splitting, as this can lead to a performance decline due to the increased number of requests required to fetch all the code chunks. Strategic code splitting, focusing on critical parts of the application, helps balance performance optimization with efficient resource loading.
88

9-
[Code Splitting Example Code](../apps/react-vite/src/app/routes/index.tsx)
9+
[Code Splitting Example Code](../apps/react-vite/src/app/router.tsx)
1010

1111
### Component and state optimizations
1212

docs/project-structure.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ src
99
| |
1010
| +-- routes # application routes / can also be called pages
1111
+-- app.tsx # main application component
12-
+-- app-provider # application provider that wraps the entire application with global providers
12+
+-- provider.tsx # application provider that wraps the entire application with different global providers
13+
+-- router.tsx # application router configuration
1314
+-- assets # assets folder can contain all the static files such as images, fonts, etc.
1415
|
1516
+-- components # shared components used across the entire application
@@ -55,6 +56,10 @@ src/features/awesome-feature
5556

5657
NOTE: You don't need all of these folders for every feature. Only include the ones that are necessary for the feature.
5758

59+
In some cases it might be more practical to keep all api calls outside of the feature folders in a dedicated `api` folder where all API calls are defined. This can be useful if you have a lot of shared api calls between features.
60+
61+
````sh
62+
5863
In the past, it was recommended to use barrel files to export all the files from a feature. However, it can cause issues for Vite to do tree shaking and can lead to performance issues. Therefore, it is recommended to import the files directly.
5964

6065
It might not be a good idea to import across the features. Instead, compose different features at the application level. This way, you can ensure that each feature is independent which makes the codebase less convoluted.
@@ -98,7 +103,7 @@ To forbid cross-feature imports, you can use ESLint:
98103
],
99104
},
100105
],
101-
```
106+
````
102107
103108
You might also want to enforce unidirectional codebase architecture. This means that the code should flow in one direction, from shared parts of the code to the application (shared -> features -> app). This is a good practice to follow as it makes the codebase more predictable and easier to understand.
104109

0 commit comments

Comments
 (0)