@jmruthers/pace-core 0.6.6 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +255 -0
  4. package/audit-tool/audits/03-architecture.cjs +196 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +291 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +241 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-7PMH7XN7.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +26 -16
  28. package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
  29. package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
  30. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  31. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  32. package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
  33. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  34. package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
  35. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  36. package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
  37. package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
  38. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  39. package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
  40. package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
  41. package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
  42. package/dist/components.d.ts +5 -4
  43. package/dist/components.js +27 -32
  44. package/dist/eslint-rules/index.cjs +22 -9
  45. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  46. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  47. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  48. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +26 -10
  49. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  50. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  51. package/dist/hooks.d.ts +5 -5
  52. package/dist/hooks.js +6 -6
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +18 -17
  55. package/dist/rbac/index.js +6 -6
  56. package/dist/theming/runtime.d.ts +14 -1
  57. package/dist/theming/runtime.js +1 -1
  58. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  59. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  60. package/dist/utils.d.ts +2 -2
  61. package/dist/utils.js +8 -8
  62. package/docs/README.md +1 -1
  63. package/docs/api/modules.md +47 -31
  64. package/docs/api-reference/components.md +18 -20
  65. package/docs/api-reference/hooks.md +80 -80
  66. package/docs/api-reference/types.md +1 -1
  67. package/docs/api-reference/utilities.md +1 -1
  68. package/docs/architecture/README.md +1 -1
  69. package/docs/core-concepts/events.md +3 -3
  70. package/docs/core-concepts/organisations.md +6 -6
  71. package/docs/core-concepts/permissions.md +6 -6
  72. package/docs/documentation-index.md +12 -18
  73. package/docs/getting-started/documentation-index.md +1 -1
  74. package/docs/getting-started/examples/README.md +4 -4
  75. package/docs/getting-started/examples/full-featured-app.md +1 -1
  76. package/docs/getting-started/faq.md +2 -2
  77. package/docs/getting-started/quick-reference.md +4 -4
  78. package/docs/implementation-guides/authentication.md +15 -15
  79. package/docs/implementation-guides/component-styling.md +1 -1
  80. package/docs/implementation-guides/data-tables.md +126 -33
  81. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  82. package/docs/implementation-guides/dynamic-colors.md +3 -3
  83. package/docs/implementation-guides/file-upload-storage.md +2 -2
  84. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  85. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  86. package/docs/implementation-guides/large-datasets.md +3 -2
  87. package/docs/implementation-guides/organisation-security.md +2 -2
  88. package/docs/implementation-guides/performance.md +2 -2
  89. package/docs/implementation-guides/permission-enforcement.md +1 -1
  90. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  91. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  92. package/docs/rbac/README.md +5 -5
  93. package/docs/rbac/advanced-patterns.md +6 -6
  94. package/docs/rbac/api-reference.md +20 -20
  95. package/docs/rbac/event-based-apps.md +3 -3
  96. package/docs/rbac/examples.md +41 -41
  97. package/docs/rbac/getting-started.md +37 -37
  98. package/docs/rbac/performance.md +1 -1
  99. package/docs/rbac/quick-start.md +52 -52
  100. package/docs/rbac/secure-client-protection.md +1 -1
  101. package/docs/rbac/troubleshooting.md +1 -1
  102. package/docs/security/README.md +5 -5
  103. package/docs/standards/0-standards-overview.md +220 -0
  104. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
  105. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  106. package/docs/standards/3-architecture-standards.md +606 -0
  107. package/docs/standards/4-code-quality-standards.md +728 -0
  108. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  109. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  110. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  111. package/docs/standards/8-testing-documentation-standards.md +401 -0
  112. package/docs/standards/9-operations-standards.md +1102 -0
  113. package/docs/standards/README.md +203 -104
  114. package/docs/troubleshooting/README.md +4 -4
  115. package/docs/troubleshooting/common-issues.md +2 -2
  116. package/docs/troubleshooting/debugging.md +9 -9
  117. package/docs/troubleshooting/migration.md +4 -4
  118. package/eslint-config-pace-core.cjs +21 -10
  119. package/package.json +6 -5
  120. package/scripts/install-cursor-rules.cjs +11 -243
  121. package/scripts/install-eslint-config.cjs +284 -0
  122. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  123. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  124. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
  125. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  126. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  127. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  128. package/src/__tests__/templates/component.test.template.tsx +18 -15
  129. package/src/components/Calendar/Calendar.tsx +201 -47
  130. package/src/components/ContextSelector/ContextSelector.tsx +137 -153
  131. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  132. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  133. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  134. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  135. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  136. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  137. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  138. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  139. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  140. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  141. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  142. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  143. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  144. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  145. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  146. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  147. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  148. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  149. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  150. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  151. package/src/components/DataTable/components/index.ts +2 -1
  152. package/src/components/DataTable/types.ts +0 -18
  153. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  154. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  155. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  156. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  157. package/src/components/Dialog/Dialog.test.tsx +1 -0
  158. package/src/components/Dialog/Dialog.tsx +25 -8
  159. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  160. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  161. package/src/components/FileUpload/FileUpload.tsx +112 -130
  162. package/src/components/Progress/Progress.tsx +2 -4
  163. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  164. package/src/components/Select/Select.tsx +86 -77
  165. package/src/components/Select/types.ts +3 -0
  166. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  167. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  168. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  169. package/src/hooks/public/usePublicEvent.ts +5 -5
  170. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  171. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  172. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  173. package/src/hooks/useAppConfig.ts +2 -2
  174. package/src/hooks/useEventTheme.test.ts +7 -7
  175. package/src/hooks/useEventTheme.ts +1 -4
  176. package/src/hooks/useFileDisplay.ts +2 -2
  177. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  178. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  179. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  180. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  181. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  182. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  183. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  184. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  185. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  186. package/src/styles/core.css +7 -0
  187. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  188. package/src/theming/parseEventColours.ts +22 -10
  189. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  190. package/src/utils/storage/README.md +1 -1
  191. package/cursor-rules/01-standards-compliance.mdc +0 -285
  192. package/cursor-rules/04-testing-standards.mdc +0 -270
  193. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  194. package/cursor-rules/06-code-quality.mdc +0 -311
  195. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  196. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  197. package/cursor-rules/11-performance-optimization.mdc +0 -169
  198. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  199. package/dist/DataTable-LRJL4IRV.js +0 -15
  200. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  201. package/dist/eslint-rules/rules/components.cjs +0 -113
  202. package/dist/eslint-rules/rules/imports.cjs +0 -102
  203. package/docs/best-practices/README.md +0 -472
  204. package/docs/best-practices/accessibility.md +0 -604
  205. package/docs/best-practices/common-patterns.md +0 -516
  206. package/docs/best-practices/deployment.md +0 -1103
  207. package/docs/best-practices/performance.md +0 -1328
  208. package/docs/best-practices/security.md +0 -940
  209. package/docs/best-practices/testing.md +0 -1034
  210. package/docs/rbac/compliance/compliance-guide.md +0 -544
  211. package/docs/standards/01-standards-compliance.md +0 -188
  212. package/docs/standards/03-solid-principles.md +0 -39
  213. package/docs/standards/04-testing-standards.md +0 -36
  214. package/docs/standards/05-bug-reports-and-features.md +0 -27
  215. package/docs/standards/06-code-quality.md +0 -34
  216. package/docs/standards/07-tech-stack-compliance.md +0 -30
  217. package/docs/standards/10-error-handling-patterns.md +0 -401
  218. package/docs/standards/11-performance-optimization.md +0 -348
  219. package/docs/standards/12-ci-cd-integration.md +0 -370
  220. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  221. package/scripts/audit/audit-compliance.cjs +0 -1295
  222. package/scripts/audit/audit-components.cjs +0 -260
  223. package/scripts/audit/audit-rbac.cjs +0 -954
  224. package/scripts/audit/audit-standards.cjs +0 -1268
  225. package/scripts/audit/index.cjs +0 -1927
  226. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  227. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  228. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  229. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  230. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  231. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  232. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  233. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  234. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  235. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  236. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  237. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  238. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  239. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  240. package/src/components/DataTable/utils/debugTools.ts +0 -514
  241. package/src/eslint-rules/index.cjs +0 -22
  242. package/src/eslint-rules/rules/components.cjs +0 -113
  243. package/src/eslint-rules/rules/imports.cjs +0 -102
  244. package/src/eslint-rules/rules/rbac.cjs +0 -790
  245. package/src/eslint-rules/utils/helpers.cjs +0 -42
  246. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -1,1328 +0,0 @@
1
- ---
2
- lastUpdated: 2025-11-18T17:00:00+11:00
3
- version: 0.5.181
4
- reviewedBy: documentation-standards-audit
5
- ---
6
-
7
- # Performance Best Practices
8
-
9
- > **📚 Complete Performance Guide** | [← Back to Documentation](./README.md) | [Installation Guide](../getting-started/installation-guide.md)
10
-
11
- Performance is crucial for user experience. This comprehensive guide provides all performance optimization techniques for `@jmruthers/pace-core` applications, from basic strategies to advanced optimization.
12
-
13
- ## Overview
14
-
15
- Performance optimization in `@jmruthers/pace-core` covers:
16
-
17
- - **Component Rendering**: Minimizing unnecessary re-renders
18
- - **Data Fetching**: Efficient data loading and caching
19
- - **Bundle Size**: Optimizing package size and tree shaking
20
- - **Memory Management**: Preventing memory leaks
21
- - **Network Optimization**: Reducing API calls and payload size
22
- - **Advanced Techniques**: Code splitting, lazy loading, and optimization tools
23
-
24
- ## Component Performance
25
-
26
- ### 1. Memoization Strategies
27
-
28
- ```typescript
29
- import { useMemo, useCallback } from 'react';
30
- import { useEvents } from '@jmruthers/pace-core';
31
-
32
- function OptimizedEventList() {
33
- const { events, loading } = useEvents();
34
-
35
- // Memoize expensive calculations
36
- const sortedEvents = useMemo(() => {
37
- return events?.sort((a, b) =>
38
- new Date(a.start_date).getTime() - new Date(b.start_date).getTime()
39
- ) || [];
40
- }, [events]);
41
-
42
- // Memoize filtered data
43
- const upcomingEvents = useMemo(() => {
44
- const now = new Date();
45
- return sortedEvents.filter(event =>
46
- new Date(event.start_date) > now
47
- );
48
- }, [sortedEvents]);
49
-
50
- // Memoize event groups
51
- const eventsByDate = useMemo(() => {
52
- return upcomingEvents.reduce((groups, event) => {
53
- const date = event.start_date.split('T')[0];
54
- if (!groups[date]) groups[date] = [];
55
- groups[date].push(event);
56
- return groups;
57
- }, {});
58
- }, [upcomingEvents]);
59
-
60
- if (loading) return <div>Loading...</div>;
61
-
62
- return (
63
- <div>
64
- {Object.entries(eventsByDate).map(([date, events]) => (
65
- <div key={date}>
66
- <h3>{date}</h3>
67
- {events.map(event => (
68
- <EventCard key={event.id} event={event} />
69
- ))}
70
- </div>
71
- ))}
72
- </div>
73
- );
74
- }
75
- ```
76
-
77
- ### 2. Callback Optimization
78
-
79
- ```typescript
80
- import { useCallback } from 'react';
81
- import { useEvents } from '@jmruthers/pace-core';
82
-
83
- function EventManager() {
84
- const { events, setSelectedEvent, refreshEvents } = useEvents();
85
-
86
- // Memoize event handlers
87
- const handleEventSelect = useCallback((event) => {
88
- setSelectedEvent(event);
89
- }, [setSelectedEvent]);
90
-
91
- const handleRefresh = useCallback(() => {
92
- refreshEvents();
93
- }, [refreshEvents]);
94
-
95
- const handleEventDelete = useCallback(async (eventId) => {
96
- try {
97
- await deleteEvent(eventId);
98
- refreshEvents();
99
- } catch (error) {
100
- console.error('Failed to delete event:', error);
101
- }
102
- }, [refreshEvents]);
103
-
104
- return (
105
- <div>
106
- <button onClick={handleRefresh}>Refresh</button>
107
- {events.map(event => (
108
- <EventCard
109
- key={event.id}
110
- event={event}
111
- onSelect={handleEventSelect}
112
- onDelete={handleEventDelete}
113
- />
114
- ))}
115
- </div>
116
- );
117
- }
118
- ```
119
-
120
- ### 3. Component Splitting
121
-
122
- ```typescript
123
- // Split large components into smaller, focused components
124
- function EventDashboard() {
125
- const { events, loading } = useEvents();
126
-
127
- if (loading) return <LoadingSpinner />;
128
-
129
- return (
130
- <div className="dashboard">
131
- <EventSummary events={events} />
132
- <EventCalendar events={events} />
133
- <EventList events={events} />
134
- </div>
135
- );
136
- }
137
-
138
- // Separate components for better performance
139
- const EventSummary = React.memo(({ events }) => {
140
- const stats = useMemo(() => ({
141
- total: events.length,
142
- upcoming: events.filter(e => new Date(e.start_date) > new Date()).length,
143
- past: events.filter(e => new Date(e.start_date) < new Date()).length,
144
- }), [events]);
145
-
146
- return (
147
- <div className="summary">
148
- <div>Total: {stats.total}</div>
149
- <div>Upcoming: {stats.upcoming}</div>
150
- <div>Past: {stats.past}</div>
151
- </div>
152
- );
153
- });
154
-
155
- const EventCalendar = React.memo(({ events }) => {
156
- // Calendar implementation
157
- return <div className="calendar">...</div>;
158
- });
159
-
160
- const EventList = React.memo(({ events }) => {
161
- // List implementation
162
- return <div className="list">...</div>;
163
- });
164
- ```
165
-
166
- ## Data Fetching Optimization
167
-
168
- ### 1. Efficient Data Loading
169
-
170
- ```typescript
171
- import { useEvents } from '@jmruthers/pace-core';
172
-
173
- function OptimizedEventLoader() {
174
- const { events, loading, error, refreshEvents } = useEvents();
175
-
176
- // Implement pagination for large datasets
177
- const [page, setPage] = useState(1);
178
- const [pageSize, setPageSize] = useState(20);
179
-
180
- const paginatedEvents = useMemo(() => {
181
- const start = (page - 1) * pageSize;
182
- const end = start + pageSize;
183
- return events.slice(start, end);
184
- }, [events, page, pageSize]);
185
-
186
- // Implement virtual scrolling for very large lists
187
- const VirtualizedEventList = useMemo(() => {
188
- return React.memo(({ events }) => {
189
- return (
190
- <div className="virtual-list">
191
- {events.map((event, index) => (
192
- <EventRow key={event.id} event={event} index={index} />
193
- ))}
194
- </div>
195
- );
196
- });
197
- }, []);
198
-
199
- return (
200
- <div>
201
- <VirtualizedEventList events={paginatedEvents} />
202
- <Pagination
203
- currentPage={page}
204
- totalPages={Math.ceil(events.length / pageSize)}
205
- onPageChange={setPage}
206
- />
207
- </div>
208
- );
209
- }
210
- ```
211
-
212
- ### 2. Caching Strategies
213
-
214
- ```typescript
215
- import { useMemoizedCallback } from '@jmruthers/pace-core';
216
-
217
- function CachedDataComponent() {
218
- const { events, refreshEvents } = useEvents();
219
-
220
- // Cache expensive operations
221
- const eventStats = useMemo(() => {
222
- return events.reduce((stats, event) => {
223
- const month = new Date(event.start_date).getMonth();
224
- stats[month] = (stats[month] || 0) + 1;
225
- return stats;
226
- }, {});
227
- }, [events]);
228
-
229
- // Cache filtered results
230
- const [filter, setFilter] = useState('');
231
- const filteredEvents = useMemo(() => {
232
- if (!filter) return events;
233
- return events.filter(event =>
234
- event.name.toLowerCase().includes(filter.toLowerCase())
235
- );
236
- }, [events, filter]);
237
-
238
- // Debounced search
239
- const debouncedSetFilter = useMemoizedCallback(
240
- (value) => setFilter(value),
241
- [],
242
- 300
243
- );
244
-
245
- return (
246
- <div>
247
- <input
248
- type="text"
249
- placeholder="Search events..."
250
- onChange={(e) => debouncedSetFilter(e.target.value)}
251
- />
252
- <EventList events={filteredEvents} />
253
- <EventStats stats={eventStats} />
254
- </div>
255
- );
256
- }
257
- ```
258
-
259
- ### 3. Background Data Updates
260
-
261
- ```typescript
262
- import { useEffect, useRef } from 'react';
263
- import { useEvents } from '@jmruthers/pace-core';
264
-
265
- function AutoRefreshingEventList() {
266
- const { events, refreshEvents } = useEvents();
267
- const intervalRef = useRef(null);
268
-
269
- // Auto-refresh data in background
270
- useEffect(() => {
271
- intervalRef.current = setInterval(() => {
272
- refreshEvents();
273
- }, 30000); // Refresh every 30 seconds
274
-
275
- return () => {
276
- if (intervalRef.current) {
277
- clearInterval(intervalRef.current);
278
- }
279
- };
280
- }, [refreshEvents]);
281
-
282
- // Pause refresh when tab is not visible
283
- useEffect(() => {
284
- const handleVisibilityChange = () => {
285
- if (document.hidden) {
286
- if (intervalRef.current) {
287
- clearInterval(intervalRef.current);
288
- }
289
- } else {
290
- intervalRef.current = setInterval(() => {
291
- refreshEvents();
292
- }, 30000);
293
- }
294
- };
295
-
296
- document.addEventListener('visibilitychange', handleVisibilityChange);
297
- return () => {
298
- document.removeEventListener('visibilitychange', handleVisibilityChange);
299
- };
300
- }, [refreshEvents]);
301
-
302
- return <EventList events={events} />;
303
- }
304
- ```
305
-
306
- ## Bundle Size Optimization
307
-
308
- ### 1. Tree Shaking
309
-
310
- ```typescript
311
- // Import only what you need
312
- import { Button, DataTable } from '@jmruthers/pace-core';
313
- // Instead of: import * from '@jmruthers/pace-core';
314
-
315
- // Use dynamic imports for large components
316
- const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
317
-
318
- function App() {
319
- return (
320
- <Suspense fallback={<div>Loading...</div>}>
321
- <HeavyComponent />
322
- </Suspense>
323
- );
324
- }
325
- ```
326
-
327
- ### 2. Code Splitting
328
-
329
- ```typescript
330
- // Split routes by feature
331
- const AdminDashboard = React.lazy(() => import('./AdminDashboard'));
332
- const UserDashboard = React.lazy(() => import('./UserDashboard'));
333
-
334
- function App() {
335
- const { user } = useUnifiedAuth();
336
-
337
- return (
338
- <Suspense fallback={<div>Loading dashboard...</div>}>
339
- {user?.role === 'admin' ? <AdminDashboard /> : <UserDashboard />}
340
- </Suspense>
341
- );
342
- }
343
- ```
344
-
345
- ### 3. Component Lazy Loading
346
-
347
- ```typescript
348
- import { useCan } from '@jmruthers/pace-core/rbac';
349
- import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
350
-
351
- // Lazy load components based on user permissions
352
- function ConditionalComponent({ permission, children }) {
353
- const { user, selectedOrganisationId } = useUnifiedAuth();
354
- const { can, isLoading } = useCan(
355
- user?.id || '',
356
- { organisationId: selectedOrganisationId || '', eventId: undefined, appId: undefined },
357
- permission
358
- );
359
- const [shouldLoad, setShouldLoad] = useState(false);
360
-
361
- useEffect(() => {
362
- if (!isLoading && can) {
363
- setShouldLoad(true);
364
- }
365
- }, [can, isLoading]);
366
-
367
- if (!shouldLoad) return null;
368
-
369
- return <Suspense fallback={<div>Loading...</div>}>{children}</Suspense>;
370
- }
371
-
372
- // Usage
373
- <ConditionalComponent permission="admin:analytics">
374
- <AnalyticsDashboard />
375
- </ConditionalComponent>
376
- ```
377
-
378
- ## Memory Management
379
-
380
- ### 1. Cleanup on Unmount
381
-
382
- ```typescript
383
- import { useEffect, useRef } from 'react';
384
- import { useEvents } from '@jmruthers/pace-core';
385
-
386
- function EventMonitor() {
387
- const { events } = useEvents();
388
- const mountedRef = useRef(true);
389
-
390
- useEffect(() => {
391
- return () => {
392
- mountedRef.current = false;
393
- };
394
- }, []);
395
-
396
- useEffect(() => {
397
- if (!mountedRef.current) return;
398
-
399
- const timer = setInterval(() => {
400
- if (mountedRef.current) {
401
- // Update logic here
402
- console.log('Events updated:', events.length);
403
- }
404
- }, 5000);
405
-
406
- return () => {
407
- clearInterval(timer);
408
- };
409
- }, [events]);
410
-
411
- return <div>Monitoring {events.length} events</div>;
412
- }
413
- ```
414
-
415
- ### 2. Event Listener Cleanup
416
-
417
- ```typescript
418
- import { useEffect } from 'react';
419
- import { useUnifiedAuth } from '@jmruthers/pace-core';
420
-
421
- function AuthListener() {
422
- const { user, signOut } = useUnifiedAuth();
423
-
424
- useEffect(() => {
425
- const handleStorageChange = (e) => {
426
- if (e.key === 'supabase.auth.token' && !e.newValue) {
427
- // Token was removed, sign out
428
- signOut();
429
- }
430
- };
431
-
432
- window.addEventListener('storage', handleStorageChange);
433
- return () => {
434
- window.removeEventListener('storage', handleStorageChange);
435
- };
436
- }, [signOut]);
437
-
438
- return null;
439
- }
440
- ```
441
-
442
- ### 3. Large List Optimization
443
-
444
- ```typescript
445
- import { useVirtualizer } from '@tanstack/react-virtual';
446
- import { useEvents } from '@jmruthers/pace-core';
447
-
448
- function VirtualizedEventList() {
449
- const { events } = useEvents();
450
- const parentRef = useRef();
451
-
452
- const rowVirtualizer = useVirtualizer({
453
- count: events.length,
454
- getScrollElement: () => parentRef.current,
455
- estimateSize: () => 100,
456
- });
457
-
458
- return (
459
- <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
460
- <div
461
- style={{
462
- height: `${rowVirtualizer.getTotalSize()}px`,
463
- width: '100%',
464
- position: 'relative',
465
- }}
466
- >
467
- {rowVirtualizer.getVirtualItems().map((virtualRow) => {
468
- const event = events[virtualRow.index];
469
- return (
470
- <div
471
- key={event.id}
472
- style={{
473
- position: 'absolute',
474
- top: 0,
475
- left: 0,
476
- width: '100%',
477
- height: `${virtualRow.size}px`,
478
- transform: `translateY(${virtualRow.start}px)`,
479
- }}
480
- >
481
- <EventCard event={event} />
482
- </div>
483
- );
484
- })}
485
- </div>
486
- </div>
487
- );
488
- }
489
- ```
490
-
491
- ## Network Optimization
492
-
493
- ### RBAC Performance Optimizations
494
-
495
- The RBAC system includes built-in performance optimizations that significantly reduce network requests:
496
-
497
- - **Request Deduplication**: Multiple components checking the same permission share network requests
498
- - **Enhanced Caching**: Two-tier caching (60s short-term + 5min session cache)
499
- - **Batched Audit Logging**: Audit events are batched to reduce network requests
500
- - **Memoization**: Components are memoized to prevent unnecessary re-renders
501
-
502
- See the [RBAC Performance Guide](../rbac/performance.md) for detailed information.
503
-
504
- ### 1. Request Deduplication
505
-
506
- ```typescript
507
- import { useMemoizedCallback } from '@jmruthers/pace-core';
508
-
509
- function OptimizedDataFetcher() {
510
- const { supabase } = useSupabase();
511
- const requestCache = useRef(new Map());
512
-
513
- const fetchEventData = useMemoizedCallback(async (eventId) => {
514
- // Check cache first
515
- if (requestCache.current.has(eventId)) {
516
- return requestCache.current.get(eventId);
517
- }
518
-
519
- // Make request
520
- const { data, error } = await supabase
521
- .from('events')
522
- .select('*')
523
- .eq('id', eventId)
524
- .single();
525
-
526
- if (error) throw error;
527
-
528
- // Cache result
529
- requestCache.current.set(eventId, data);
530
- return data;
531
- }, [supabase]);
532
-
533
- return (
534
- <div>
535
- {eventIds.map(id => (
536
- <EventDetails key={id} eventId={id} onFetch={fetchEventData} />
537
- ))}
538
- </div>
539
- );
540
- }
541
- ```
542
-
543
- ### 2. Batch Requests
544
-
545
- ```typescript
546
- import { useSupabase } from '@jmruthers/pace-core';
547
-
548
- function BatchDataFetcher() {
549
- const { supabase } = useSupabase();
550
- const [events, setEvents] = useState([]);
551
- const [loading, setLoading] = useState(false);
552
-
553
- const fetchEventsBatch = async (eventIds) => {
554
- setLoading(true);
555
- try {
556
- const { data, error } = await supabase
557
- .from('events')
558
- .select('*')
559
- .in('id', eventIds);
560
-
561
- if (error) throw error;
562
- setEvents(data);
563
- } catch (error) {
564
- console.error('Failed to fetch events:', error);
565
- } finally {
566
- setLoading(false);
567
- }
568
- };
569
-
570
- return (
571
- <div>
572
- {loading && <div>Loading...</div>}
573
- <EventList events={events} />
574
- </div>
575
- );
576
- }
577
- ```
578
-
579
- ### 3. Progressive Loading
580
-
581
- ```typescript
582
- import { useState, useEffect } from 'react';
583
- import { useEvents } from '@jmruthers/pace-core';
584
-
585
- function ProgressiveEventList() {
586
- const { events } = useEvents();
587
- const [visibleEvents, setVisibleEvents] = useState([]);
588
- const [page, setPage] = useState(1);
589
- const pageSize = 10;
590
-
591
- useEffect(() => {
592
- const start = 0;
593
- const end = page * pageSize;
594
- setVisibleEvents(events.slice(start, end));
595
- }, [events, page]);
596
-
597
- const loadMore = () => {
598
- setPage(prev => prev + 1);
599
- };
600
-
601
- const hasMore = visibleEvents.length < events.length;
602
-
603
- return (
604
- <div>
605
- {visibleEvents.map(event => (
606
- <EventCard key={event.id} event={event} />
607
- ))}
608
- {hasMore && (
609
- <button onClick={loadMore}>
610
- Load More ({events.length - visibleEvents.length} remaining)
611
- </button>
612
- )}
613
- </div>
614
- );
615
- }
616
- ```
617
-
618
- ## Performance Monitoring
619
-
620
- ### 1. Component Performance Tracking
621
-
622
- ```typescript
623
- import { useComponentPerformance } from '@jmruthers/pace-core';
624
-
625
- function PerformanceMonitor() {
626
- const { renderCount, averageRenderTime, lastRenderTime } = useComponentPerformance();
627
-
628
- useEffect(() => {
629
- if (averageRenderTime > 16) { // 60fps threshold
630
- console.warn('Component rendering slowly:', averageRenderTime);
631
- }
632
- }, [averageRenderTime]);
633
-
634
- return (
635
- <div className="performance-monitor">
636
- <div>Renders: {renderCount}</div>
637
- <div>Avg Time: {averageRenderTime.toFixed(2)}ms</div>
638
- <div>Last Render: {lastRenderTime.toFixed(2)}ms</div>
639
- </div>
640
- );
641
- }
642
- ```
643
-
644
- ### 2. Network Performance
645
-
646
- ```typescript
647
- import { useUnifiedAuth } from '@jmruthers/pace-core';
648
-
649
- function NetworkMonitor() {
650
- const { error } = useUnifiedAuth();
651
- const [networkStats, setNetworkStats] = useState({
652
- requests: 0,
653
- errors: 0,
654
- avgResponseTime: 0,
655
- });
656
-
657
- useEffect(() => {
658
- const startTime = performance.now();
659
-
660
- return () => {
661
- const endTime = performance.now();
662
- const responseTime = endTime - startTime;
663
-
664
- setNetworkStats(prev => ({
665
- ...prev,
666
- requests: prev.requests + 1,
667
- avgResponseTime: (prev.avgResponseTime + responseTime) / 2,
668
- }));
669
- };
670
- }, []);
671
-
672
- return (
673
- <div className="network-monitor">
674
- <div>Requests: {networkStats.requests}</div>
675
- <div>Errors: {networkStats.errors}</div>
676
- <div>Avg Response: {networkStats.avgResponseTime.toFixed(2)}ms</div>
677
- </div>
678
- );
679
- }
680
- ```
681
-
682
- ### 3. Memory Usage Monitoring
683
-
684
- ```typescript
685
- function MemoryMonitor() {
686
- const [memoryInfo, setMemoryInfo] = useState(null);
687
-
688
- useEffect(() => {
689
- const updateMemoryInfo = () => {
690
- if ('memory' in performance) {
691
- setMemoryInfo({
692
- used: performance.memory.usedJSHeapSize,
693
- total: performance.memory.totalJSHeapSize,
694
- limit: performance.memory.jsHeapSizeLimit,
695
- });
696
- }
697
- };
698
-
699
- const interval = setInterval(updateMemoryInfo, 5000);
700
- updateMemoryInfo();
701
-
702
- return () => clearInterval(interval);
703
- }, []);
704
-
705
- if (!memoryInfo) return null;
706
-
707
- const usagePercent = (memoryInfo.used / memoryInfo.limit) * 100;
708
-
709
- return (
710
- <div className="memory-monitor">
711
- <div>Memory Usage: {usagePercent.toFixed(1)}%</div>
712
- <div>Used: {(memoryInfo.used / 1024 / 1024).toFixed(1)}MB</div>
713
- <div>Total: {(memoryInfo.total / 1024 / 1024).toFixed(1)}MB</div>
714
- </div>
715
- );
716
- }
717
- ```
718
-
719
- ## Performance Best Practices Checklist
720
-
721
- ### Development Checklist
722
-
723
- - [ ] Use React.memo for expensive components
724
- - [ ] Memoize expensive calculations with useMemo
725
- - [ ] Memoize event handlers with useCallback
726
- - [ ] Implement proper cleanup in useEffect
727
- - [ ] Use lazy loading for large components
728
- - [ ] Implement virtual scrolling for large lists
729
- - [ ] Cache expensive API calls
730
- - [ ] Debounce user input
731
- - [ ] Optimize bundle size with tree shaking
732
- - [ ] Monitor component render performance
733
-
734
- ### Production Checklist
735
-
736
- - [ ] Enable production builds
737
- - [ ] Implement proper error boundaries
738
- - [ ] Set up performance monitoring
739
- - [ ] Optimize images and assets
740
- - [ ] Configure proper caching headers
741
- - [ ] Implement service workers for offline support
742
- - [ ] Monitor Core Web Vitals
743
- - [ ] Set up automated performance testing
744
- - [ ] Configure CDN for static assets
745
- - [ ] Implement progressive loading
746
-
747
- ### Monitoring Checklist
748
-
749
- - [ ] Track component render times
750
- - [ ] Monitor API response times
751
- - [ ] Track memory usage
752
- - [ ] Monitor bundle size
753
- - [ ] Set up performance alerts
754
- - [ ] Track user experience metrics
755
- - [ ] Monitor error rates
756
- - [ ] Track loading times
757
- - [ ] Monitor Core Web Vitals
758
- - [ ] Set up automated performance reports
759
-
760
- ## Performance Tools
761
-
762
- ### 1. React DevTools Profiler
763
-
764
- ```typescript
765
- import { Profiler } from 'react';
766
-
767
- function ProfiledComponent() {
768
- const onRenderCallback = (id, phase, actualDuration) => {
769
- console.log(`Component ${id} took ${actualDuration}ms to ${phase}`);
770
- };
771
-
772
- return (
773
- <Profiler id="EventList" onRender={onRenderCallback}>
774
- <EventList />
775
- </Profiler>
776
- );
777
- }
778
- ```
779
-
780
- ### 2. Custom Performance Hooks
781
-
782
- ```typescript
783
- function usePerformanceMonitor(componentName) {
784
- const renderCount = useRef(0);
785
- const startTime = useRef(performance.now());
786
-
787
- useEffect(() => {
788
- renderCount.current += 1;
789
- const renderTime = performance.now() - startTime.current;
790
-
791
- console.log(`${componentName} rendered in ${renderTime.toFixed(2)}ms`);
792
- startTime.current = performance.now();
793
- });
794
- }
795
- ```
796
-
797
- ### 3. Performance Budgets
798
-
799
- ```typescript
800
- // Set performance budgets
801
- const PERFORMANCE_BUDGETS = {
802
- firstContentfulPaint: 1500, // 1.5s
803
- largestContentfulPaint: 2500, // 2.5s
804
- firstInputDelay: 100, // 100ms
805
- cumulativeLayoutShift: 0.1, // 0.1
806
- };
807
-
808
- function checkPerformanceBudget(metrics) {
809
- Object.entries(PERFORMANCE_BUDGETS).forEach(([metric, budget]) => {
810
- if (metrics[metric] > budget) {
811
- console.warn(`${metric} exceeded budget: ${metrics[metric]} > ${budget}`);
812
- }
813
- });
814
- }
815
- ```
816
-
817
- ## Advanced Performance Techniques
818
-
819
- ### Bundle Optimization
820
-
821
- #### Tree Shaking
822
-
823
- **Goal**: Minimize bundle size by eliminating unused code.
824
-
825
- **Implementation**:
826
- ```typescript
827
- // ✅ Good - Named exports for tree shaking
828
- export { Button } from './Button';
829
- export { Card } from './Card';
830
- export { DataTable } from './DataTable';
831
-
832
- // ❌ Bad - Default exports reduce tree shaking
833
- export default { Button, Card, DataTable };
834
- ```
835
-
836
- **Verify Tree Shaking**:
837
- ```bash
838
- # Check bundle size
839
- npm run build
840
- npx vite-bundle-visualizer
841
- ```
842
-
843
- #### Dynamic Imports
844
-
845
- **Use Case**: Load components only when needed.
846
-
847
- ```tsx
848
- import React, { Suspense, lazy } from 'react';
849
-
850
- // Lazy load heavy components
851
- const DataTable = lazy(() => import('@jmruthers/pace-core').then(m => ({ default: m.DataTable })));
852
- const ChartEditor = lazy(() => import('./components/ChartEditor'));
853
-
854
- function App() {
855
- return (
856
- <Suspense fallback={<LoadingSpinner />}>
857
- <DataTable data={data} columns={columns} />
858
- </Suspense>
859
- );
860
- }
861
- ```
862
-
863
- #### Analysis Tools
864
-
865
- ```bash
866
- # Install analyzer
867
- npm install -D vite-bundle-visualizer
868
-
869
- # Analyze bundle
870
- npx vite-bundle-visualizer dist/stats.html
871
- ```
872
-
873
- ### Advanced Component Memoization
874
-
875
- #### React.memo for Expensive Components
876
-
877
- ```tsx
878
- import React from 'react';
879
- import { Card } from '@jmruthers/pace-core';
880
-
881
- interface UserCardProps {
882
- user: User;
883
- onEdit: (userId: string) => void;
884
- }
885
-
886
- // Memoize expensive components
887
- const UserCard = React.memo(({ user, onEdit }: UserCardProps) => {
888
- return (
889
- <Card className="p-4">
890
- <h3>{user.name}</h3>
891
- <p>{user.email}</p>
892
- <button onClick={() => onEdit(user.id)}>Edit</button>
893
- </Card>
894
- );
895
- });
896
-
897
- // Custom comparison function for complex props
898
- const ComplexComponent = React.memo(
899
- ({ data, config }: { data: any[]; config: any }) => {
900
- return <DataTable data={data} columns={config.columns} />;
901
- },
902
- (prevProps, nextProps) => {
903
- // Only re-render if data length or config changed
904
- return (
905
- prevProps.data.length === nextProps.data.length &&
906
- JSON.stringify(prevProps.config) === JSON.stringify(nextProps.config)
907
- );
908
- }
909
- );
910
- ```
911
-
912
- #### useMemo for Expensive Calculations
913
-
914
- ```tsx
915
- import { useMemo } from 'react';
916
- import { DataTable } from '@jmruthers/pace-core';
917
-
918
- function ExpensiveDataProcessor({ rawData }: { rawData: any[] }) {
919
- // Memoize expensive data processing
920
- const processedData = useMemo(() => {
921
- return rawData.map(item => ({
922
- ...item,
923
- // Expensive calculations
924
- score: calculateComplexScore(item),
925
- category: categorizeItem(item),
926
- processed: true
927
- }));
928
- }, [rawData]);
929
-
930
- // Memoize column configuration
931
- const columns = useMemo(() => [
932
- { accessorKey: 'name', header: 'Name' },
933
- { accessorKey: 'score', header: 'Score' },
934
- { accessorKey: 'category', header: 'Category' }
935
- ], []);
936
-
937
- return <DataTable data={processedData} columns={columns} />;
938
- }
939
- ```
940
-
941
- ### Memory Management
942
-
943
- #### Preventing Memory Leaks
944
-
945
- ```tsx
946
- import { useEffect, useRef } from 'react';
947
-
948
- function DataFetcher() {
949
- const isMountedRef = useRef(true);
950
-
951
- useEffect(() => {
952
- const fetchData = async () => {
953
- try {
954
- const data = await api.getData();
955
- if (isMountedRef.current) {
956
- setData(data);
957
- }
958
- } catch (error) {
959
- if (isMountedRef.current) {
960
- setError(error);
961
- }
962
- }
963
- };
964
-
965
- fetchData();
966
-
967
- return () => {
968
- isMountedRef.current = false;
969
- };
970
- }, []);
971
-
972
- // Cleanup subscriptions
973
- useEffect(() => {
974
- const subscription = eventBus.subscribe('dataUpdate', handleUpdate);
975
-
976
- return () => {
977
- subscription.unsubscribe();
978
- };
979
- }, []);
980
- }
981
- ```
982
-
983
- #### Efficient State Updates
984
-
985
- ```tsx
986
- import { useCallback, useReducer } from 'react';
987
-
988
- // Use reducer for complex state
989
- function dataReducer(state: any, action: any) {
990
- switch (action.type) {
991
- case 'SET_DATA':
992
- return { ...state, data: action.payload };
993
- case 'UPDATE_ITEM':
994
- return {
995
- ...state,
996
- data: state.data.map(item =>
997
- item.id === action.id ? { ...item, ...action.updates } : item
998
- )
999
- };
1000
- case 'DELETE_ITEM':
1001
- return {
1002
- ...state,
1003
- data: state.data.filter(item => item.id !== action.id)
1004
- };
1005
- default:
1006
- return state;
1007
- }
1008
- }
1009
-
1010
- function OptimizedDataManager() {
1011
- const [state, dispatch] = useReducer(dataReducer, { data: [], loading: false });
1012
-
1013
- // Memoize dispatch functions
1014
- const updateItem = useCallback((id: string, updates: any) => {
1015
- dispatch({ type: 'UPDATE_ITEM', id, updates });
1016
- }, []);
1017
-
1018
- const deleteItem = useCallback((id: string) => {
1019
- dispatch({ type: 'DELETE_ITEM', id });
1020
- }, []);
1021
-
1022
- return (
1023
- <DataTable
1024
- data={state.data}
1025
- onUpdate={updateItem}
1026
- onDelete={deleteItem}
1027
- />
1028
- );
1029
- }
1030
- ```
1031
-
1032
- ### Network Optimization
1033
-
1034
- #### Request Deduplication
1035
-
1036
- ```tsx
1037
- import { useMemo } from 'react';
1038
-
1039
- // Deduplicate identical requests
1040
- const requestCache = new Map();
1041
-
1042
- function useOptimizedFetch(url: string, options: any) {
1043
- const cacheKey = useMemo(() =>
1044
- `${url}-${JSON.stringify(options)}`,
1045
- [url, options]
1046
- );
1047
-
1048
- const fetchData = useCallback(async () => {
1049
- if (requestCache.has(cacheKey)) {
1050
- return requestCache.get(cacheKey);
1051
- }
1052
-
1053
- const promise = fetch(url, options).then(res => res.json());
1054
- requestCache.set(cacheKey, promise);
1055
-
1056
- // Clear cache after 5 minutes
1057
- setTimeout(() => {
1058
- requestCache.delete(cacheKey);
1059
- }, 5 * 60 * 1000);
1060
-
1061
- return promise;
1062
- }, [cacheKey]);
1063
-
1064
- return fetchData;
1065
- }
1066
- ```
1067
-
1068
- #### Batch API Calls
1069
-
1070
- ```tsx
1071
- import { useCallback } from 'react';
1072
-
1073
- function useBatchRequests() {
1074
- const batchRequests = useCallback(async (requests: any[]) => {
1075
- // Group requests by endpoint
1076
- const groupedRequests = requests.reduce((groups, request) => {
1077
- const key = request.endpoint;
1078
- if (!groups[key]) groups[key] = [];
1079
- groups[key].push(request);
1080
- return groups;
1081
- }, {});
1082
-
1083
- // Execute batches in parallel
1084
- const results = await Promise.all(
1085
- Object.entries(groupedRequests).map(([endpoint, batch]) =>
1086
- api.batchRequest(endpoint, batch)
1087
- )
1088
- );
1089
-
1090
- return results.flat();
1091
- }, []);
1092
-
1093
- return batchRequests;
1094
- }
1095
- ```
1096
-
1097
- ### Code Splitting Strategies
1098
-
1099
- #### Route-Based Splitting
1100
-
1101
- ```tsx
1102
- import { lazy, Suspense } from 'react';
1103
- import { Routes, Route } from 'react-router-dom';
1104
-
1105
- // Lazy load route components
1106
- const Dashboard = lazy(() => import('./pages/Dashboard'));
1107
- const Users = lazy(() => import('./pages/Users'));
1108
- const Settings = lazy(() => import('./pages/Settings'));
1109
-
1110
- function App() {
1111
- return (
1112
- <Suspense fallback={<LoadingSpinner />}>
1113
- <Routes>
1114
- <Route path="/" element={<Dashboard />} />
1115
- <Route path="/users" element={<Users />} />
1116
- <Route path="/settings" element={<Settings />} />
1117
- </Routes>
1118
- </Suspense>
1119
- );
1120
- }
1121
- ```
1122
-
1123
- #### Feature-Based Splitting
1124
-
1125
- ```tsx
1126
- import { lazy, Suspense } from 'react';
1127
- import { useCan } from '@jmruthers/pace-core';
1128
-
1129
- // Lazy load admin features
1130
- const AdminPanel = lazy(() => import('./AdminPanel'));
1131
- const UserManagement = lazy(() => import('./UserManagement'));
1132
-
1133
- function App() {
1134
- const { hasPermission } = useCan();
1135
-
1136
- return (
1137
- <div>
1138
- <MainContent />
1139
-
1140
- {hasPermission('admin:access') && (
1141
- <Suspense fallback={<div>Loading admin features...</div>}>
1142
- <AdminPanel />
1143
- </Suspense>
1144
- )}
1145
-
1146
- {hasPermission('users:manage') && (
1147
- <Suspense fallback={<div>Loading user management...</div>}>
1148
- <UserManagement />
1149
- </Suspense>
1150
- )}
1151
- </div>
1152
- );
1153
- }
1154
- ```
1155
-
1156
- ### Performance Monitoring
1157
-
1158
- #### Real-Time Performance Tracking
1159
-
1160
- ```tsx
1161
- import { useEffect } from 'react';
1162
-
1163
- function usePerformanceMonitor() {
1164
- useEffect(() => {
1165
- // Monitor Core Web Vitals
1166
- const observer = new PerformanceObserver((list) => {
1167
- for (const entry of list.getEntries()) {
1168
- console.log('Performance metric:', {
1169
- name: entry.name,
1170
- value: entry.value,
1171
- startTime: entry.startTime
1172
- });
1173
- }
1174
- });
1175
-
1176
- observer.observe({ entryTypes: ['measure', 'navigation'] });
1177
-
1178
- return () => observer.disconnect();
1179
- }, []);
1180
- }
1181
-
1182
- // Usage in components
1183
- function MyComponent() {
1184
- usePerformanceMonitor();
1185
-
1186
- return <div>Component content</div>;
1187
- }
1188
- ```
1189
-
1190
- #### Bundle Size Monitoring
1191
-
1192
- ```bash
1193
- # Add to package.json scripts
1194
- {
1195
- "scripts": {
1196
- "analyze": "npm run build && npx vite-bundle-visualizer",
1197
- "size-check": "npm run build && npx bundlesize"
1198
- }
1199
- }
1200
-
1201
- # Monitor bundle size
1202
- npm run analyze
1203
- ```
1204
-
1205
- ### Performance Testing
1206
-
1207
- #### Automated Performance Tests
1208
-
1209
- ```typescript
1210
- // performance.test.ts
1211
- import { render, screen } from '@testing-library/react';
1212
- import { performance } from 'perf_hooks';
1213
-
1214
- describe('Performance Tests', () => {
1215
- test('DataTable renders within performance budget', async () => {
1216
- const start = performance.now();
1217
-
1218
- render(<DataTable data={largeDataset} columns={columns} />);
1219
-
1220
- await screen.findByRole('table');
1221
-
1222
- const end = performance.now();
1223
- const renderTime = end - start;
1224
-
1225
- // Should render within 100ms
1226
- expect(renderTime).toBeLessThan(100);
1227
- });
1228
-
1229
- test('Component re-renders are optimized', () => {
1230
- let renderCount = 0;
1231
-
1232
- const TestComponent = () => {
1233
- renderCount++;
1234
- return <div>Test</div>;
1235
- };
1236
-
1237
- const { rerender } = render(<TestComponent />);
1238
-
1239
- // Re-render with same props
1240
- rerender(<TestComponent />);
1241
-
1242
- // Should not cause unnecessary re-renders
1243
- expect(renderCount).toBe(1);
1244
- });
1245
- });
1246
- ```
1247
-
1248
- ## Performance Checklist
1249
-
1250
- ### Development Phase
1251
- - [ ] Use React.memo for expensive components
1252
- - [ ] Implement useCallback for event handlers
1253
- - [ ] Use useMemo for expensive calculations
1254
- - [ ] Implement proper cleanup in useEffect
1255
- - [ ] Use dynamic imports for large components
1256
- - [ ] Optimize bundle with tree shaking
1257
-
1258
- ### Production Phase
1259
- - [ ] Enable production optimizations
1260
- - [ ] Monitor Core Web Vitals
1261
- - [ ] Implement performance budgets
1262
- - [ ] Set up performance monitoring
1263
- - [ ] Test with real data volumes
1264
- - [ ] Optimize images and assets
1265
-
1266
- ### Monitoring Phase
1267
- - [ ] Track bundle size over time
1268
- - [ ] Monitor render performance
1269
- - [ ] Watch for memory leaks
1270
- - [ ] Analyze user experience metrics
1271
- - [ ] Set up performance alerts
1272
- - [ ] Regular performance audits
1273
-
1274
- ## Related Documentation
1275
-
1276
- - [Data Tables Performance](../implementation-guides/data-tables.md#performance-optimization) - DataTable-specific optimizations
1277
- - [Authentication Performance](../implementation-guides/authentication.md#performance) - Auth-related performance
1278
- - [Best Practices Overview](./README.md) - General best practices
1279
- - [Troubleshooting Performance Issues](../troubleshooting/README.md#performance-issues) - Performance debugging
1280
-
1281
- ## ♿ Accessibility
1282
-
1283
- Performance optimizations should maintain accessibility:
1284
-
1285
- - **Lazy loading doesn't break screen readers** - Ensure lazy-loaded content is accessible
1286
- - **Code splitting maintains keyboard navigation** - Verify keyboard navigation works after code splitting
1287
- - **Memoization doesn't affect focus management** - Ensure focus handling remains correct
1288
- - **Performance optimizations are tested with assistive technologies** - Verify optimizations don't break accessibility
1289
- - **Loading states are announced** - Screen readers should announce performance-related loading states
1290
-
1291
- ### Accessibility Best Practices
1292
-
1293
- 1. **Test optimizations with screen readers** - Verify performance improvements don't break accessibility
1294
- 2. **Ensure keyboard navigation** - All optimized components should remain keyboard accessible
1295
- 3. **Maintain focus visibility** - Focus indicators should remain visible after optimizations
1296
- 4. **Test with assistive technologies** - Verify performance optimizations work with screen readers
1297
- 5. **Balance performance and accessibility** - Don't sacrifice accessibility for performance gains
1298
-
1299
- ## ⚠️ Edge Cases
1300
-
1301
- ### Performance vs Accessibility Trade-offs
1302
-
1303
- When performance optimizations impact accessibility:
1304
- - Find balance between performance and accessibility
1305
- - Test optimizations with assistive technologies
1306
- - Ensure lazy loading doesn't break screen reader navigation
1307
- - Verify code splitting maintains keyboard navigation
1308
- - Document trade-offs when necessary
1309
-
1310
- ### Over-Optimization Issues
1311
-
1312
- When over-optimization causes problems:
1313
- - Monitor for premature optimization
1314
- - Test optimizations in real-world scenarios
1315
- - Profile before and after optimizations
1316
- - Verify optimizations provide measurable benefits
1317
- - Consider maintainability when optimizing
1318
-
1319
- ### Performance Regression
1320
-
1321
- When performance degrades after optimizations:
1322
- - Profile to identify bottlenecks
1323
- - Test with realistic data volumes
1324
- - Monitor performance metrics
1325
- - Rollback optimizations if needed
1326
- - Document performance regression causes
1327
-
1328
- For more information about optimizing your application, see the [Security Guide](./security.md) and [Testing Guide](./testing.md).