@jmruthers/pace-core 0.5.19 → 0.5.21
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.
- package/dist/{DataTable-ZEQDWNKA.js → DataTable-IX7N7XYG.js} +4 -4
- package/dist/{chunk-3ZVV6URZ.js → chunk-37WAIATW.js} +4 -4
- package/dist/{chunk-7UEIZCST.js → chunk-4TMM2IGR.js} +5 -4
- package/dist/chunk-4TMM2IGR.js.map +1 -0
- package/dist/{chunk-DKDTXS5Q.js → chunk-JE7K6Q3N.js} +10 -9
- package/dist/chunk-JE7K6Q3N.js.map +1 -0
- package/dist/{chunk-CLEIIMJO.js → chunk-R4QSIQDW.js} +2 -2
- package/dist/{chunk-J42NVDVM.js → chunk-ULP6OIQE.js} +42 -8
- package/dist/chunk-ULP6OIQE.js.map +1 -0
- package/dist/{chunk-CMXBNDPM.js → chunk-UYKKHRJN.js} +2 -2
- package/dist/{chunk-7EGP6KZT.js → chunk-YA77BOZM.js} +17 -17
- package/dist/chunk-YA77BOZM.js.map +1 -0
- package/dist/{chunk-DV2Z3RBQ.js → chunk-ZIYFAQJ5.js} +2 -2
- package/dist/components.js +6 -6
- package/dist/hooks.js +2 -2
- package/dist/index.js +8 -8
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +3 -3
- package/dist/utils.js +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +7 -7
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +2 -2
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +12 -12
- package/docs/troubleshooting/cake-infinite-rerender-debugging.md +284 -0
- package/docs/troubleshooting/cake-infinite-rerender-summary.md +117 -0
- package/docs/troubleshooting/cake-rerender-diagnostic.js +162 -0
- package/docs/troubleshooting/rbac-critical-fixes-summary.md +260 -0
- package/package.json +1 -1
- package/src/__tests__/hooks/usePermissions.test.ts +265 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +187 -0
- package/src/hooks/useAppConfig.ts +7 -6
- package/src/providers/EventProvider.tsx +4 -2
- package/src/rbac/components/PagePermissionGuard.tsx +56 -13
- package/src/rbac/hooks/usePermissions.ts +21 -14
- package/dist/chunk-7EGP6KZT.js.map +0 -1
- package/dist/chunk-7UEIZCST.js.map +0 -1
- package/dist/chunk-DKDTXS5Q.js.map +0 -1
- package/dist/chunk-J42NVDVM.js.map +0 -1
- /package/dist/{DataTable-ZEQDWNKA.js.map → DataTable-IX7N7XYG.js.map} +0 -0
- /package/dist/{chunk-3ZVV6URZ.js.map → chunk-37WAIATW.js.map} +0 -0
- /package/dist/{chunk-CLEIIMJO.js.map → chunk-R4QSIQDW.js.map} +0 -0
- /package/dist/{chunk-CMXBNDPM.js.map → chunk-UYKKHRJN.js.map} +0 -0
- /package/dist/{chunk-DV2Z3RBQ.js.map → chunk-ZIYFAQJ5.js.map} +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Cake Infinite Re-render Debugging Guide
|
|
2
|
+
|
|
3
|
+
## 🚨 Issue: Infinite Re-renders in Meals Component
|
|
4
|
+
|
|
5
|
+
The Cake team is experiencing infinite re-renders in their Meals component, causing the `PagePermissionGuard` to re-render continuously. The diagnostic logs show:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
[Meals] Diagnostic info:
|
|
9
|
+
Object { renderCount: 1, instanceCount: 0 }
|
|
10
|
+
[Meals] Diagnostic info:
|
|
11
|
+
Object { renderCount: 2, instanceCount: 0 }
|
|
12
|
+
[Meals] Diagnostic info:
|
|
13
|
+
Object { renderCount: 3, instanceCount: 0 }
|
|
14
|
+
...
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 🔍 Root Cause Analysis
|
|
18
|
+
|
|
19
|
+
The issue is **NOT** in `PagePermissionGuard` - it's working correctly:
|
|
20
|
+
- ✅ App ID resolution: `Successfully resolved app ID: a0865252-b745-4639-8028-a53ee6128000`
|
|
21
|
+
- ✅ Permissions granted: `Final decision: { hasPermission: true }`
|
|
22
|
+
- ✅ No multiple instances: `instanceCount: 0`
|
|
23
|
+
|
|
24
|
+
The problem is in the **parent Meals component** that's re-rendering infinitely.
|
|
25
|
+
|
|
26
|
+
## 🛠️ Debugging Steps
|
|
27
|
+
|
|
28
|
+
### Step 1: Add Comprehensive Debugging to Meals Component
|
|
29
|
+
|
|
30
|
+
Add this debugging code to your Meals component:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import React, { useMemo, useCallback, useEffect, useRef } from 'react';
|
|
34
|
+
|
|
35
|
+
const Meals = React.memo(() => {
|
|
36
|
+
const renderCountRef = useRef(0);
|
|
37
|
+
renderCountRef.current += 1;
|
|
38
|
+
|
|
39
|
+
// Track what's causing re-renders
|
|
40
|
+
const [debugInfo, setDebugInfo] = useState({
|
|
41
|
+
renderCount: renderCountRef.current,
|
|
42
|
+
lastRenderTime: Date.now(),
|
|
43
|
+
reRenderReasons: []
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Get all your hooks
|
|
47
|
+
const eventsData = useEvents();
|
|
48
|
+
const authData = useUnifiedAuth();
|
|
49
|
+
const orgData = useOrganisations();
|
|
50
|
+
const configData = useAppConfig();
|
|
51
|
+
|
|
52
|
+
// Track changes in each hook
|
|
53
|
+
const prevEventsRef = useRef(eventsData);
|
|
54
|
+
const prevAuthRef = useRef(authData);
|
|
55
|
+
const prevOrgRef = useRef(orgData);
|
|
56
|
+
const prevConfigRef = useRef(configData);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const reasons = [];
|
|
60
|
+
|
|
61
|
+
// Check if events data changed
|
|
62
|
+
if (prevEventsRef.current !== eventsData) {
|
|
63
|
+
reasons.push('eventsData changed');
|
|
64
|
+
prevEventsRef.current = eventsData;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check if auth data changed
|
|
68
|
+
if (prevAuthRef.current !== authData) {
|
|
69
|
+
reasons.push('authData changed');
|
|
70
|
+
prevAuthRef.current = authData;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if org data changed
|
|
74
|
+
if (prevOrgRef.current !== orgData) {
|
|
75
|
+
reasons.push('orgData changed');
|
|
76
|
+
prevOrgRef.current = orgData;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check if config data changed
|
|
80
|
+
if (prevConfigRef.current !== configData) {
|
|
81
|
+
reasons.push('configData changed');
|
|
82
|
+
prevConfigRef.current = configData;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log('[Meals] Re-render reasons:', reasons);
|
|
86
|
+
console.log('[Meals] Current data:', {
|
|
87
|
+
eventsData: eventsData?.selectedEvent?.id,
|
|
88
|
+
authData: authData?.user?.id,
|
|
89
|
+
orgData: orgData?.selectedOrganisation?.id,
|
|
90
|
+
configData: configData?.requiresEvent
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Your existing component logic...
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<PagePermissionGuard
|
|
98
|
+
pageName="meals"
|
|
99
|
+
operation="read"
|
|
100
|
+
fallback={<div>Loading permissions...</div>}
|
|
101
|
+
>
|
|
102
|
+
{/* Your DataTable content */}
|
|
103
|
+
</PagePermissionGuard>
|
|
104
|
+
);
|
|
105
|
+
}, (prevProps, nextProps) => {
|
|
106
|
+
// This should never re-render since there are no props
|
|
107
|
+
console.log('[Meals] React.memo comparison called - this should not happen!');
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Step 2: Check Hook Dependencies
|
|
113
|
+
|
|
114
|
+
Look for these common causes of infinite re-renders:
|
|
115
|
+
|
|
116
|
+
#### A. Unstable Object References
|
|
117
|
+
```typescript
|
|
118
|
+
// ❌ BAD - Creates new object on every render
|
|
119
|
+
const data = { events: eventsData, auth: authData };
|
|
120
|
+
|
|
121
|
+
// ✅ GOOD - Memoize the object
|
|
122
|
+
const data = useMemo(() => ({
|
|
123
|
+
events: eventsData,
|
|
124
|
+
auth: authData
|
|
125
|
+
}), [eventsData, authData]);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### B. Unstable Function References
|
|
129
|
+
```typescript
|
|
130
|
+
// ❌ BAD - Creates new function on every render
|
|
131
|
+
const handleClick = () => { /* ... */ };
|
|
132
|
+
|
|
133
|
+
// ✅ GOOD - Memoize the function
|
|
134
|
+
const handleClick = useCallback(() => { /* ... */ }, []);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### C. useEffect Dependencies
|
|
138
|
+
```typescript
|
|
139
|
+
// ❌ BAD - Missing dependencies or unstable dependencies
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
// ...
|
|
142
|
+
}, []); // Missing dependencies
|
|
143
|
+
|
|
144
|
+
// ❌ BAD - Unstable dependency
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
// ...
|
|
147
|
+
}, [someObject]); // someObject changes every render
|
|
148
|
+
|
|
149
|
+
// ✅ GOOD - Stable dependencies
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
// ...
|
|
152
|
+
}, [userId, eventId]); // Primitive values
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Step 3: Check Parent Components
|
|
156
|
+
|
|
157
|
+
The infinite re-render might be coming from a parent component. Check:
|
|
158
|
+
|
|
159
|
+
1. **Parent component re-renders**: Add debugging to parent components
|
|
160
|
+
2. **Context providers**: Check if any context providers are re-rendering
|
|
161
|
+
3. **State updates**: Look for state updates that trigger re-renders
|
|
162
|
+
|
|
163
|
+
### Step 4: Use React DevTools Profiler
|
|
164
|
+
|
|
165
|
+
1. Open React DevTools
|
|
166
|
+
2. Go to Profiler tab
|
|
167
|
+
3. Start recording
|
|
168
|
+
4. Interact with the component
|
|
169
|
+
5. Stop recording
|
|
170
|
+
6. Look for components that re-render frequently
|
|
171
|
+
|
|
172
|
+
### Step 5: Check for State Updates in Render
|
|
173
|
+
|
|
174
|
+
Look for state updates that happen during render:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ❌ BAD - State update during render
|
|
178
|
+
const Meals = () => {
|
|
179
|
+
const [count, setCount] = useState(0);
|
|
180
|
+
|
|
181
|
+
// This will cause infinite re-renders!
|
|
182
|
+
setCount(count + 1);
|
|
183
|
+
|
|
184
|
+
return <div>{count}</div>;
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 🔧 Common Fixes
|
|
189
|
+
|
|
190
|
+
### Fix 1: Memoize Expensive Calculations
|
|
191
|
+
```typescript
|
|
192
|
+
const Meals = React.memo(() => {
|
|
193
|
+
const eventsData = useEvents();
|
|
194
|
+
const authData = useUnifiedAuth();
|
|
195
|
+
|
|
196
|
+
// Memoize expensive calculations
|
|
197
|
+
const processedData = useMemo(() => {
|
|
198
|
+
return processEventsData(eventsData);
|
|
199
|
+
}, [eventsData]);
|
|
200
|
+
|
|
201
|
+
// Memoize callbacks
|
|
202
|
+
const handleEventChange = useCallback((eventId) => {
|
|
203
|
+
// Handle event change
|
|
204
|
+
}, []);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<PagePermissionGuard pageName="meals" operation="read">
|
|
208
|
+
{/* Your content */}
|
|
209
|
+
</PagePermissionGuard>
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Fix 2: Stabilize Hook Dependencies
|
|
215
|
+
```typescript
|
|
216
|
+
const Meals = React.memo(() => {
|
|
217
|
+
const eventsData = useEvents();
|
|
218
|
+
const authData = useUnifiedAuth();
|
|
219
|
+
|
|
220
|
+
// Extract only the values you need
|
|
221
|
+
const selectedEventId = eventsData?.selectedEvent?.id;
|
|
222
|
+
const userId = authData?.user?.id;
|
|
223
|
+
|
|
224
|
+
// Use primitive values in dependencies
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
// Your effect logic
|
|
227
|
+
}, [selectedEventId, userId]);
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<PagePermissionGuard pageName="meals" operation="read">
|
|
231
|
+
{/* Your content */}
|
|
232
|
+
</PagePermissionGuard>
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Fix 3: Check Context Providers
|
|
238
|
+
Make sure context providers are not re-rendering:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// ❌ BAD - Creates new object every render
|
|
242
|
+
const AppProvider = ({ children }) => {
|
|
243
|
+
const [state, setState] = useState({});
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<AppContext.Provider value={{ state, setState }}>
|
|
247
|
+
{children}
|
|
248
|
+
</AppContext.Provider>
|
|
249
|
+
);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// ✅ GOOD - Memoize the context value
|
|
253
|
+
const AppProvider = ({ children }) => {
|
|
254
|
+
const [state, setState] = useState({});
|
|
255
|
+
|
|
256
|
+
const contextValue = useMemo(() => ({
|
|
257
|
+
state,
|
|
258
|
+
setState
|
|
259
|
+
}), [state]);
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<AppContext.Provider value={contextValue}>
|
|
263
|
+
{children}
|
|
264
|
+
</AppContext.Provider>
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## 🎯 Next Steps
|
|
270
|
+
|
|
271
|
+
1. **Add the debugging code** to your Meals component
|
|
272
|
+
2. **Run the app** and check the console logs
|
|
273
|
+
3. **Identify the re-render reasons** from the logs
|
|
274
|
+
4. **Apply the appropriate fix** based on what's causing the re-renders
|
|
275
|
+
5. **Test the fix** to ensure the infinite re-renders stop
|
|
276
|
+
|
|
277
|
+
## 📞 Need Help?
|
|
278
|
+
|
|
279
|
+
If you're still experiencing issues after following these steps, please share:
|
|
280
|
+
1. The console logs from the debugging code
|
|
281
|
+
2. The Meals component code
|
|
282
|
+
3. Any parent component code that might be relevant
|
|
283
|
+
|
|
284
|
+
The issue is definitely in the Cake application code, not in pace-core, so we need to identify what's causing the parent component to re-render infinitely.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Cake Infinite Re-render Issue - Summary & Action Plan
|
|
2
|
+
|
|
3
|
+
## 🚨 Current Status
|
|
4
|
+
|
|
5
|
+
The Cake team is experiencing **infinite re-renders** in their Meals component, causing the `PagePermissionGuard` to re-render continuously. This is **NOT** a pace-core issue - it's a Cake application issue.
|
|
6
|
+
|
|
7
|
+
## 📊 Evidence from Console Logs
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[Meals] Diagnostic info:
|
|
11
|
+
Object { renderCount: 1, instanceCount: 0 }
|
|
12
|
+
[Meals] Diagnostic info:
|
|
13
|
+
Object { renderCount: 2, instanceCount: 0 }
|
|
14
|
+
[Meals] Diagnostic info:
|
|
15
|
+
Object { renderCount: 3, instanceCount: 0 }
|
|
16
|
+
...
|
|
17
|
+
[Meals] Diagnostic info:
|
|
18
|
+
Object { renderCount: 19, instanceCount: 0 }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Key Observations:**
|
|
22
|
+
- ✅ `PagePermissionGuard` is working correctly (permissions granted, app ID resolved)
|
|
23
|
+
- ✅ No multiple instances (`instanceCount: 0`)
|
|
24
|
+
- ❌ Parent Meals component is re-rendering infinitely (`renderCount: 1, 2, 3...`)
|
|
25
|
+
|
|
26
|
+
## 🔍 Root Cause
|
|
27
|
+
|
|
28
|
+
The infinite re-renders are caused by the **parent Meals component**, not by `PagePermissionGuard`. The parent component is re-rendering continuously, which triggers `PagePermissionGuard` to re-render as well.
|
|
29
|
+
|
|
30
|
+
## 🛠️ Immediate Action Plan
|
|
31
|
+
|
|
32
|
+
### Step 1: Add Debugging Code
|
|
33
|
+
Use the diagnostic script provided in `cake-rerender-diagnostic.js` to identify what's causing the re-renders.
|
|
34
|
+
|
|
35
|
+
### Step 2: Common Causes to Check
|
|
36
|
+
|
|
37
|
+
1. **Unstable Object References**
|
|
38
|
+
```typescript
|
|
39
|
+
// ❌ BAD - Creates new object every render
|
|
40
|
+
const data = { events: eventsData, auth: authData };
|
|
41
|
+
|
|
42
|
+
// ✅ GOOD - Memoize
|
|
43
|
+
const data = useMemo(() => ({ events: eventsData, auth: authData }), [eventsData, authData]);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. **Unstable Function References**
|
|
47
|
+
```typescript
|
|
48
|
+
// ❌ BAD - Creates new function every render
|
|
49
|
+
const handleClick = () => { /* ... */ };
|
|
50
|
+
|
|
51
|
+
// ✅ GOOD - Memoize
|
|
52
|
+
const handleClick = useCallback(() => { /* ... */ }, []);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
3. **useEffect Dependencies**
|
|
56
|
+
```typescript
|
|
57
|
+
// ❌ BAD - Missing or unstable dependencies
|
|
58
|
+
useEffect(() => { /* ... */ }, []);
|
|
59
|
+
useEffect(() => { /* ... */ }, [someObject]);
|
|
60
|
+
|
|
61
|
+
// ✅ GOOD - Stable dependencies
|
|
62
|
+
useEffect(() => { /* ... */ }, [userId, eventId]);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
4. **State Updates During Render**
|
|
66
|
+
```typescript
|
|
67
|
+
// ❌ BAD - Causes infinite re-renders
|
|
68
|
+
const [count, setCount] = useState(0);
|
|
69
|
+
setCount(count + 1); // Don't do this!
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
5. **Context Provider Re-renders**
|
|
73
|
+
```typescript
|
|
74
|
+
// ❌ BAD - Creates new value every render
|
|
75
|
+
<Context.Provider value={{ state, setState }}>
|
|
76
|
+
|
|
77
|
+
// ✅ GOOD - Memoize the value
|
|
78
|
+
const value = useMemo(() => ({ state, setState }), [state]);
|
|
79
|
+
<Context.Provider value={value}>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Step 3: Use React DevTools
|
|
83
|
+
1. Open React DevTools Profiler
|
|
84
|
+
2. Record a session
|
|
85
|
+
3. Look for components that re-render frequently
|
|
86
|
+
4. Identify the root cause
|
|
87
|
+
|
|
88
|
+
## 📁 Files to Check
|
|
89
|
+
|
|
90
|
+
1. **Meals component** - Add the debugging code
|
|
91
|
+
2. **Parent components** - Check for re-render causes
|
|
92
|
+
3. **Context providers** - Ensure they're not re-rendering
|
|
93
|
+
4. **Custom hooks** - Check for unstable dependencies
|
|
94
|
+
|
|
95
|
+
## 🎯 Expected Outcome
|
|
96
|
+
|
|
97
|
+
After applying the fixes:
|
|
98
|
+
- Render count should stabilize at 1-2 renders
|
|
99
|
+
- No more infinite re-renders
|
|
100
|
+
- `PagePermissionGuard` should work correctly
|
|
101
|
+
- DataTable should display properly
|
|
102
|
+
|
|
103
|
+
## 📞 Next Steps
|
|
104
|
+
|
|
105
|
+
1. **Immediate**: Add the debugging code to identify the cause
|
|
106
|
+
2. **Short-term**: Apply the appropriate fixes based on the debugging output
|
|
107
|
+
3. **Long-term**: Review the entire component tree for similar issues
|
|
108
|
+
|
|
109
|
+
## 🔗 Resources
|
|
110
|
+
|
|
111
|
+
- **Debugging Guide**: `cake-infinite-rerender-debugging.md`
|
|
112
|
+
- **Diagnostic Script**: `cake-rerender-diagnostic.js`
|
|
113
|
+
- **React DevTools**: https://react.dev/learn/react-developer-tools
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
**Remember**: This is a Cake application issue, not a pace-core issue. The `PagePermissionGuard` is working correctly - the problem is in the parent component that's re-rendering infinitely.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Cake Infinite Re-render Diagnostic Script
|
|
2
|
+
// Add this to your Meals component to identify the cause of infinite re-renders
|
|
3
|
+
|
|
4
|
+
import React, { useMemo, useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
// Add this hook to track re-render causes
|
|
7
|
+
const useRenderTracker = (componentName) => {
|
|
8
|
+
const renderCountRef = useRef(0);
|
|
9
|
+
const prevPropsRef = useRef({});
|
|
10
|
+
const prevStateRef = useRef({});
|
|
11
|
+
|
|
12
|
+
renderCountRef.current += 1;
|
|
13
|
+
|
|
14
|
+
const [debugInfo, setDebugInfo] = useState({
|
|
15
|
+
renderCount: renderCountRef.current,
|
|
16
|
+
lastRenderTime: Date.now(),
|
|
17
|
+
reRenderReasons: []
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const trackRender = (currentProps, currentState) => {
|
|
21
|
+
const reasons = [];
|
|
22
|
+
|
|
23
|
+
// Check if props changed
|
|
24
|
+
if (prevPropsRef.current !== currentProps) {
|
|
25
|
+
reasons.push('props changed');
|
|
26
|
+
prevPropsRef.current = currentProps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if state changed
|
|
30
|
+
if (prevStateRef.current !== currentState) {
|
|
31
|
+
reasons.push('state changed');
|
|
32
|
+
prevStateRef.current = currentState;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If no obvious reason, it might be parent re-render
|
|
36
|
+
if (reasons.length === 0) {
|
|
37
|
+
reasons.push('parent re-render');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(`[${componentName}] Re-render #${renderCountRef.current}:`, {
|
|
41
|
+
reasons,
|
|
42
|
+
timestamp: new Date().toISOString(),
|
|
43
|
+
props: currentProps,
|
|
44
|
+
state: currentState
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
setDebugInfo({
|
|
48
|
+
renderCount: renderCountRef.current,
|
|
49
|
+
lastRenderTime: Date.now(),
|
|
50
|
+
reRenderReasons: reasons
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return { trackRender, debugInfo };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Enhanced Meals component with debugging
|
|
58
|
+
const MealsWithDebugging = React.memo(() => {
|
|
59
|
+
const { trackRender, debugInfo } = useRenderTracker('Meals');
|
|
60
|
+
|
|
61
|
+
// Get all your hooks
|
|
62
|
+
const eventsData = useEvents();
|
|
63
|
+
const authData = useUnifiedAuth();
|
|
64
|
+
const orgData = useOrganisations();
|
|
65
|
+
const configData = useAppConfig();
|
|
66
|
+
|
|
67
|
+
// Track hook changes
|
|
68
|
+
const prevEventsRef = useRef(eventsData);
|
|
69
|
+
const prevAuthRef = useRef(authData);
|
|
70
|
+
const prevOrgRef = useRef(orgData);
|
|
71
|
+
const prevConfigRef = useRef(configData);
|
|
72
|
+
|
|
73
|
+
// Check what's changing
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const hookChanges = [];
|
|
76
|
+
|
|
77
|
+
if (prevEventsRef.current !== eventsData) {
|
|
78
|
+
hookChanges.push('eventsData changed');
|
|
79
|
+
console.log('[Meals] eventsData changed:', {
|
|
80
|
+
old: prevEventsRef.current,
|
|
81
|
+
new: eventsData
|
|
82
|
+
});
|
|
83
|
+
prevEventsRef.current = eventsData;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (prevAuthRef.current !== authData) {
|
|
87
|
+
hookChanges.push('authData changed');
|
|
88
|
+
console.log('[Meals] authData changed:', {
|
|
89
|
+
old: prevAuthRef.current,
|
|
90
|
+
new: authData
|
|
91
|
+
});
|
|
92
|
+
prevAuthRef.current = authData;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (prevOrgRef.current !== orgData) {
|
|
96
|
+
hookChanges.push('orgData changed');
|
|
97
|
+
console.log('[Meals] orgData changed:', {
|
|
98
|
+
old: prevOrgRef.current,
|
|
99
|
+
new: orgData
|
|
100
|
+
});
|
|
101
|
+
prevOrgRef.current = orgData;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (prevConfigRef.current !== configData) {
|
|
105
|
+
hookChanges.push('configData changed');
|
|
106
|
+
console.log('[Meals] configData changed:', {
|
|
107
|
+
old: prevConfigRef.current,
|
|
108
|
+
new: configData
|
|
109
|
+
});
|
|
110
|
+
prevConfigRef.current = configData;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (hookChanges.length > 0) {
|
|
114
|
+
console.log('[Meals] Hook changes detected:', hookChanges);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Track render
|
|
119
|
+
trackRender({}, {});
|
|
120
|
+
|
|
121
|
+
// Your existing component logic...
|
|
122
|
+
const selectedEvent = useMemo(() => eventsData.selectedEvent, [eventsData.selectedEvent?.event_id, eventsData.selectedEvent?.id]);
|
|
123
|
+
const user = useMemo(() => authData.user, [authData.user?.id]);
|
|
124
|
+
const selectedOrganisation = useMemo(() => orgData.selectedOrganisation, [orgData.selectedOrganisation?.id]);
|
|
125
|
+
const requiresEvent = useMemo(() => configData.requiresEvent, [configData.requiresEvent]);
|
|
126
|
+
|
|
127
|
+
const eventId = useMemo(() => {
|
|
128
|
+
return selectedEvent?.event_id || selectedEvent?.id;
|
|
129
|
+
}, [selectedEvent?.event_id, selectedEvent?.id]);
|
|
130
|
+
|
|
131
|
+
// Log diagnostic info
|
|
132
|
+
console.log('[Meals] Diagnostic info:', {
|
|
133
|
+
renderCount: debugInfo.renderCount,
|
|
134
|
+
reRenderReasons: debugInfo.reRenderReasons,
|
|
135
|
+
hookValues: {
|
|
136
|
+
selectedEventId: selectedEvent?.id,
|
|
137
|
+
userId: user?.id,
|
|
138
|
+
organisationId: selectedOrganisation?.id,
|
|
139
|
+
requiresEvent
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<PagePermissionGuard
|
|
145
|
+
pageName="meals"
|
|
146
|
+
operation="read"
|
|
147
|
+
fallback={<div>Loading permissions...</div>}
|
|
148
|
+
>
|
|
149
|
+
<h1>Meals</h1>
|
|
150
|
+
<p>Manage the meals available for {selectedEvent?.event_name}</p>
|
|
151
|
+
{/* Your DataTable content */}
|
|
152
|
+
</PagePermissionGuard>
|
|
153
|
+
);
|
|
154
|
+
}, (prevProps, nextProps) => {
|
|
155
|
+
// This should never be called since there are no props
|
|
156
|
+
console.log('[Meals] React.memo comparison called - this indicates a problem!');
|
|
157
|
+
console.log('[Meals] prevProps:', prevProps);
|
|
158
|
+
console.log('[Meals] nextProps:', nextProps);
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export default MealsWithDebugging;
|