feat(context): drop componentName param from context consumer hooks - #3267
Conversation
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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the βοΈ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
Codecov Reportβ Patch coverage is 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. π New features to boost your workflow:
|
β¦#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.
π― Goal
REACT-1026
Context hooks accepted a
componentNameargument used only to name a component in aconsole.warnwhen 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 anduseTranslationContextcould 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 passedComponent.name, which minifies away.BREAKING CHANGE:
componentNameargument.useChatContext('X')and friends stop type-checking; drop the argument.{}. Affects the 14 hooks listed above. Components must be rendered within the provider they read from β e.g. anything callinguseChatContextneeds a<Chat>ancestor.useChannelInstanceContextreturnsPartial<ChannelInstanceContextValue>βchannelis now typed as possiblyundefined.ChatViewContexthas no default value (typed| undefined), souseContext(ChatViewContext)may returnundefined.MessageComposerContextis typed asMessageComposerContextValue | undefined, correcting a narrower declaration that was papered over with casts.ComponentContext.MessageTimestampoverride; 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 Tfallback deferred failures to a downstreamCannot read properties of undefinedseveral frames away. Everything now goes throughrequireContext(src/context/requireContext.ts):useChatContext,useMessageContext,useMessageComposerContext,useMessageListContext,useVirtualizedMessageListContext,useMessageBounceContext,usePollContext,useDialogManager,useSearchContext,useSearchSourceResultsContext,useContextMenuContext,useChannelListItemContext,useGalleryContext,useChatViewContextuseTranslationContext,useComponentContext,useChannelInstanceContext(nowPartial),useModalContext,useAriaLiveAnnouncer,useMessageTranslationViewContextAudio,VoiceRecording,CardAudio,Timestamp,MessageRepliesCountButton,MessageComposerUI,ModalGalleryTwo changes were needed before their hooks could throw:
WithDragAndDropUploaddetected the composer viaObject.keys(ctx).length > 0; now uses auseIsWithinMessageComposerContext()predicate. Adds the test file this component lacked.GalleryHeaderread message context while rendering outside anyMessageProvider.GalleryItemnow carriesuserandcreatedAtβ wrapping the call sites would be wrong, sinceChannelMediaViewflattens many messages into one list. Adds atimestamp.GalleryTimestampkey.ChatViewContextalso loses its module-levelLayoutControllerdefault, which let unrelated subtrees write to one shared instance.src/context/__tests__/missingProviderContract.test.tsxpins 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.