# Error Fix: "Objects are not valid as a React child"
## Problem
When creating a custom request through the NewRequestWizard, the application threw an error:
```
Error: Objects are not valid as a React child (found: object with keys {email, name, level, tat, tatType})
```
## Root Cause
The NewRequestWizard stores approvers, spectators, ccList, and invitedUsers as arrays of objects with the structure:
```typescript
{
email: string,
name: string,
level: number,
tat: number,
tatType: 'hours' | 'days'
}
```
When these objects were passed to `handleNewRequestSubmit` in App.tsx, they were being mapped to the request structure, but the mapping wasn't properly extracting the string values from the objects. Instead, entire objects were being assigned to fields that should contain strings.
## Solution
### 1. Enhanced Approver Mapping
Updated the `approvalFlow` mapping in `handleNewRequestSubmit` to properly extract values:
```typescript
approvalFlow: (requestData.approvers || [])
.filter((a: any) => a) // Filter out null/undefined
.map((approver: any, index: number) => {
// Extract name from email if name is not available
const approverName = approver?.name || approver?.email?.split('@')[0] || `Approver ${index + 1}`;
const approverEmail = approver?.email || '';
return {
step: index + 1,
approver: `${approverName}${approverEmail ? ` (${approverEmail})` : ''}`, // STRING, not object
role: approver?.role || `Level ${approver?.level || index + 1} Approver`,
status: index === 0 ? 'pending' : 'waiting',
tatHours: approver?.tat ? (typeof approver.tat === 'string' ? parseInt(approver.tat) : approver.tat) : 48,
elapsedHours: index === 0 ? 0 : 0,
assignedAt: index === 0 ? new Date().toISOString() : null,
comment: null,
timestamp: null
};
})
```
**Key changes:**
- Added `.filter((a: any) => a)` to remove null/undefined entries
- Properly extract `approverName` from `name` or `email`
- Build a display string combining name and email
- Convert TAT to number (handles both string and number inputs)
### 2. Enhanced Spectator Mapping
Updated spectators mapping to properly extract values:
```typescript
spectators: (requestData.spectators || [])
.filter((s: any) => s && (s.name || s.email)) // Filter invalid entries
.map((spectator: any) => {
const name = spectator?.name || spectator?.email?.split('@')[0] || 'Observer';
return {
name: name, // STRING, not object
role: spectator?.role || spectator?.department || 'Observer',
avatar: name.split(' ').map((n: string) => n[0]).join('').toUpperCase().slice(0, 2) || 'OB'
};
})
```
**Key changes:**
- Filter out entries without name or email
- Extract name from email if needed
- Safe avatar generation with fallback
### 3. Added Missing Fields
Added fields required by MyRequests component:
```typescript
currentApprover: requestData.approvers?.[0]?.name || requestData.approvers?.[0]?.email?.split('@')[0] || 'Pending Assignment',
approverLevel: `1 of ${requestData.approvers?.length || 1}`,
submittedDate: new Date().toISOString(),
estimatedCompletion: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
```
### 4. Fixed Audit Trail Message
Updated audit trail to safely extract approver name:
```typescript
details: `Request assigned to ${requestData.approvers?.[0]?.name || requestData.approvers?.[0]?.email || 'first approver'}`
```
## Testing Checklist
✅ **Create Custom Request**
- [ ] Fill out NewRequestWizard with title, description
- [ ] Add 2-3 approvers with emails
- [ ] Add spectators (optional)
- [ ] Submit request
✅ **Verify No Errors**
- [ ] No console errors about "Objects are not valid as React child"
- [ ] Request appears in My Requests
- [ ] Can click on request
✅ **Verify Detail Page**
- [ ] Request detail page loads
- [ ] Approver names display correctly (not [object Object])
- [ ] Workflow tab shows all approvers
- [ ] Spectators display correctly (if added)
✅ **Verify My Requests Display**
- [ ] Request shows in list
- [ ] Current approver displays as string
- [ ] Approver level shows correctly (e.g., "1 of 3")
## Common Patterns to Avoid
### ❌ Bad: Rendering Objects Directly
```typescript
{approver} // If approver is {email: "...", name: "..."}
```
### ✅ Good: Extract String First
```typescript
{approver.name || approver.email}
```
### ❌ Bad: Assigning Object to String Field
```typescript
{
approver: approverObject // {email: "...", name: "..."}
}
```
### ✅ Good: Extract String Value
```typescript
{
approver: approverObject.name || approverObject.email
}
```
## Related Files Modified
1. **App.tsx** - `handleNewRequestSubmit` function
- Enhanced approver mapping
- Enhanced spectator mapping
- Added missing fields for MyRequests compatibility
- Fixed audit trail messages
## Data Flow
```
NewRequestWizard (formData)
├── approvers: [{email, name, level, tat, tatType}, ...]
├── spectators: [{email, name, role, department}, ...]
└── ...other fields
↓
handleNewRequestSubmit (App.tsx)
├── Maps approvers → approvalFlow with STRING values
├── Maps spectators → spectators with STRING values
└── Creates complete request object
↓
dynamicRequests state (App.tsx)
├── Stored in memory
└── Passed to components
↓
RequestDetail / MyRequests
├── Receives proper data structure
└── Renders strings (no object errors)
```
## Prevention Tips
1. **Always validate data types** when mapping from wizard to database
2. **Extract primitive values** from objects before assigning to display fields
3. **Add TypeScript interfaces** to catch type mismatches early
4. **Test with console.log** before rendering to verify data structure
5. **Use optional chaining** (`?.`) to safely access nested properties
## Future Improvements
1. Add TypeScript interfaces for wizard form data
2. Add TypeScript interfaces for request objects
3. Create validation functions for data transformation
4. Add unit tests for data mapping functions
5. Create reusable mapping utilities