Skip to content

fix: Replace profile image onError DOM manipulation with React stateΒ #189

@hessius

Description

@hessius

Priority: 🟒 Low β€” Target: post-v2.0.0

Summary

Both ControlCenter.tsx and ControlCenterExpanded.tsx use direct DOM manipulation in their profile image onError handlers instead of React state. This is fragile and relies on DOM element ordering.

Current Implementation

Both files use identical patterns:

<img
  src={profileImageUrl}
  onError={e => {
    (e.target as HTMLImageElement).style.display = 'none';
    (e.target as HTMLImageElement).nextElementSibling?.classList.remove('hidden')
  }}
/>
<Coffee className="hidden" weight="fill" /> {/* Fallback icon */}

Problems

  1. Relies on DOM sibling order β€” if the JSX structure changes, nextElementSibling may point to the wrong element
  2. Direct DOM mutation β€” style.display = 'none' and classList.remove bypass React's reconciliation
  3. Not testable β€” React Testing Library can't easily verify DOM mutations made outside React
  4. Duplicated β€” Same pattern in both ControlCenter and ControlCenterExpanded

Proposed Solution

Use React state to control visibility:

const [imgError, setImgError] = useState(false)

// In render:
{!imgError && profileImageUrl ? (
  <img
    src={profileImageUrl}
    onError={() => setImgError(true)}
    alt={profileName}
  />
) : (
  <Coffee weight="fill" />
)}

If the useProfileImage hook from #184 is implemented, this state can be encapsulated there:

function useProfileImage(profileName: string | null) {
  const [url, setUrl] = useState<string | null>(null)
  const [error, setError] = useState(false)
  // ... fetch logic
  return { url, error, onError: () => setError(true) }
}

Files to Modify

Acceptance Criteria

  • No direct DOM manipulation (style.display, classList) in image error handlers
  • Fallback icon renders correctly when image fails to load
  • Fallback works when profileImageUrl is null/empty
  • No layout shift when switching from image to fallback
  • TypeScript compiles cleanly

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions