Skip to content

feat(context): drop componentName param from context consumer hooks - #3267

Merged
oliverlaz merged 2 commits into
release-v15from
feat/context-hook-cleanup
Aug 17, 2026
Merged

feat(context): drop componentName param from context consumer hooks#3267
oliverlaz merged 2 commits into
release-v15from
feat/context-hook-cleanup

Conversation

@oliverlaz

@oliverlaz oliverlaz commented Aug 17, 2026

Copy link
Copy Markdown
Member

🎯 Goal

REACT-1026

Context hooks accepted a componentName argument used only to name a component in a console.warn when the hook was called outside its provider. React DevTools does that better, and the diagnostic was mostly inert: of the 7 hooks accepting it, 3 ignored it and useTranslationContext could never reach its warn branch. Meanwhile it was threaded through 138 call sites and often wrong β€” 11 passed a hook name, several were stale, and 5 passed Component.name, which minifies away.

BREAKING CHANGE:

  • Context hooks no longer accept a componentName argument. useChatContext('X') and friends stop type-checking; drop the argument.
  • Required contexts throw when used outside their provider, instead of logging a warning and returning {}. Affects the 14 hooks listed above. Components must be rendered within the provider they read from β€” e.g. anything calling useChatContext needs a <Chat> ancestor.
  • useChannelInstanceContext returns Partial<ChannelInstanceContextValue> β€” channel is now typed as possibly undefined.
  • ChatViewContext has no default value (typed | undefined), so useContext(ChatViewContext) may return undefined.
  • MessageComposerContext is typed as MessageComposerContextValue | undefined, correcting a narrower declaration that was papered over with casts.
  • The gallery header no longer uses the ComponentContext.MessageTimestamp override; it renders the item's own timestamp.

πŸ›  Implementation details

Parameter removed from all 7 hooks and their 138 call sites.

Missing-provider behaviour also normalized, since src/ had three competing conventions (warn + {} as T, throw, silent cast). The {} as T fallback deferred failures to a downstream Cannot read properties of undefined several frames away. Everything now goes through requireContext (src/context/requireContext.ts):

  • Required β†’ throw, naming hook and provider: useChatContext, useMessageContext, useMessageComposerContext, useMessageListContext, useVirtualizedMessageListContext, useMessageBounceContext, usePollContext, useDialogManager, useSearchContext, useSearchSourceResultsContext, useContextMenuContext, useChannelListItemContext, useGalleryContext, useChatViewContext
  • Optional by design β†’ keep default, honest type: useTranslationContext, useComponentContext, useChannelInstanceContext (now Partial), useModalContext, useAriaLiveAnnouncer, useMessageTranslationViewContext
  • Renders both inside and outside a provider β†’ read the raw context: Audio, VoiceRecording, CardAudio, Timestamp, MessageRepliesCountButton, MessageComposerUI, ModalGallery

Two changes were needed before their hooks could throw:

  • WithDragAndDropUpload detected the composer via Object.keys(ctx).length > 0; now uses a useIsWithinMessageComposerContext() predicate. Adds the test file this component lacked.
  • GalleryHeader read message context while rendering outside any MessageProvider. GalleryItem now carries user and createdAt β€” wrapping the call sites would be wrong, since ChannelMediaView flattens many messages into one list. Adds a timestamp.GalleryTimestamp key.

ChatViewContext also loses its module-level LayoutController default, which let unrelated subtrees write to one shared instance.

src/context/__tests__/missingProviderContract.test.tsx pins the required/optional split.

🎨 UI Changes

The channel-media gallery header now shows a per-item sender and timestamp, which it previously couldn't display at all.

No screenshots: the demo users on the shared environment have no channels, and seeding one would write test data others would see. Covered by unit tests in GalleryUI.test.tsx β€” worth a manual look.

The `componentName` argument existed only to interpolate a name into a
console.warn fired when a hook was called outside its provider. React DevTools
attributes those calls far better, and the diagnostic was largely inert already:
of the 7 hooks that accepted it, 3 ignored it outright and useTranslationContext
could never reach its warn branch (its context carries a real default). Many of
the 138 call sites passed the wrong name β€” hook names, or stale component names β€”
and 5 passed `Component.name`, which a minifier mangles.

Removes the parameter from all 7 hooks and their 138 call sites, and normalizes
missing-provider behaviour behind a shared `requireContext` helper:

- Required contexts throw, naming the hook and the provider. This replaces
  `return {} as T`, a typed lie that deferred failure to an opaque
  "cannot read properties of undefined" several frames later.
- Contexts with a meaningful default keep it and gain an honest type:
  useTranslationContext, useComponentContext, useChannelInstanceContext
  (now Partial), useModalContext, useAriaLiveAnnouncer,
  useMessageTranslationViewContext.
- Components that legitimately render both inside and outside a provider read
  the raw context instead (Audio, VoiceRecording, CardAudio, Timestamp,
  MessageRepliesCountButton, MessageComposerUI, ModalGallery).

Two supporting changes were needed so their hooks could throw:

- WithDragAndDropUpload probed the `{}` fallback via
  `Object.keys(ctx).length > 0`; it now uses a
  `useIsWithinMessageComposerContext()` predicate. Adds the test file this
  component previously lacked, covering both upload modes.
- GalleryHeader read the message context while rendering outside any
  MessageProvider. GalleryItem now carries `user` and `createdAt`, so channel
  media shows a correct per-item sender and timestamp β€” which it previously
  could not display at all. Adds a `timestamp.GalleryTimestamp` key.

ChatViewContext loses its module-level LayoutController default, which silently
shared one instance across unrelated subtrees.

Contract pinned by src/context/__tests__/missingProviderContract.test.tsx.
State current behaviour rather than what changed, and drop the reasoning
narration a reviewer has no context for. Also replaces the stale `fixme` in
useDialogManager, which the missing-manager guard now handles.
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a737c6cd-64b1-4a16-a0b6-2e95bc7cc1f0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • πŸ” Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands.

@oliverlaz
oliverlaz had a problem deploying to Vite Example Public (Preview) August 17, 2026 08:20 — with GitHub Actions Error
@oliverlaz
oliverlaz had a problem deploying to Vite Example Development (Preview) August 17, 2026 08:20 — with GitHub Actions Failure
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.50549% with 10 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (release-v15@17c91bc). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/plugins/Emojis/EmojiPicker.tsx 0.00% 2 Missing ⚠️
...rc/components/Attachment/LinkPreview/CardAudio.tsx 0.00% 1 Missing ⚠️
src/components/Message/MessageBlocked.tsx 0.00% 1 Missing ⚠️
src/components/Message/StreamedMessageText.tsx 0.00% 1 Missing ⚠️
src/components/MessageComposer/icons.tsx 0.00% 1 Missing ⚠️
src/components/Thread/ThreadHead.tsx 0.00% 1 Missing ⚠️
.../Threads/ThreadList/ThreadListEmptyPlaceholder.tsx 0.00% 1 Missing ⚠️
...mponents/TypingIndicator/TypingIndicatorHeader.tsx 0.00% 1 Missing ⚠️
src/context/useChannel.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff               @@
##             release-v15    #3267   +/-   ##
==============================================
  Coverage               ?   84.28%           
==============================================
  Files                  ?      526           
  Lines                  ?    15942           
  Branches               ?     5115           
==============================================
  Hits                   ?    13436           
  Misses                 ?     2506           
  Partials               ?        0           

β˜” View full report in Codecov by Harness.
πŸ“’ Have feedback on the report? Share it here.

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@oliverlaz
oliverlaz merged commit 9194215 into release-v15 Aug 17, 2026
11 of 13 checks passed
@oliverlaz
oliverlaz deleted the feat/context-hook-cleanup branch August 17, 2026 10:23
oliverlaz added a commit that referenced this pull request Aug 17, 2026
…#3268)

### Goal


[REACT-1027](https://linear.app/stream/issue/REACT-1027/remove-the-deprecated-message-component-override-from-componentcontext)

`ComponentContext.Message` was deprecated in favour of `MessageUI`
during v14 but never removed. Removing it surfaced four more ways to
reach the same component β€” a `Message` prop on `Message`, `MessageList`,
`VirtualizedMessageList` and `Thread` β€” now all collapsed onto the
`MessageUI` slot.

BREAKING CHANGES:
- **`ComponentContext.Message` removed** β†’ use the `MessageUI` slot.
- **The `Message` prop is removed from `Message`, `MessageList`,
`VirtualizedMessageList` and `Thread`**, and therefore from
`additionalMessageListProps`, `additionalVirtualizedMessageListProps`
and `additionalParentMessageProps`. To replace a per-component prop,
scope the slot to that subtree:

  ```tsx
  // before
  <Thread Message={CustomThreadMessage} />

  // after
  <WithComponents overrides={{ MessageUI: CustomThreadMessage }}>
    <Thread />
  </WithComponents>
  ```

- **`areMessagePropsEqual` no longer compares the message UI component**
β€” it comes from context, and context updates re-render consumers
regardless of `React.memo`.

`VirtualMessage` is unchanged and still wins inside
`VirtualizedMessageList`, but now only within that list. Full migration
detail: `ai-docs/ai-migration-v14-v15.md`.

### Implementation details

`MessageProps.Message` was the transport `VirtualizedMessageList` and
`Thread` used to inject their resolved component, so deleting it naively
would have silently broken the `VirtualMessage` slot. Instead,
`VirtualizedMessageList` applies `VirtualMessage` to its own subtree's
`ComponentContext` β€” wrapping only when the slot is set β€” which also
retires `VirtuosoContext.Message`. `Thread`'s three-step resolution
collapses entirely; context already carries the component.
`WithComponents` now memoizes its merged override map: required, not
cosmetic, since an unmemoized provider on the list's render path would
defeat per-message memoization.

`VirtualMessage` had no test coverage, which is what made this risky.
Added three tests β€” precedence, subtree scoping, unset fallback β€” and
the precedence one fails on `release-v15`. They assert what
`useComponentContext()` resolves to rather than inspecting rendered
items, because Virtuoso renders zero items under jsdom and an item-level
assertion would pass either way.

Verified: `tsc -p tsconfig.lib.json --noEmit` clean Β· `yarn test` 231
files, 2828 passed / 1 skipped Β· `yarn lint-fix` clean. Each documented
failure mode was compiled to confirm it errors (`TS2322` for props,
`TS2561` for override keys).

**Unrelated change:** the v14 β†’ v15 guide also gains a section on the
context-hook `componentName` removal from #3267, included at the
author's request rather than split out.

### UI Changes

None β€” override plumbing only; the default message UI and every existing
`MessageUI` / `VirtualMessage` override render exactly as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants