Skip to content

feat(react-native-host): extend RNXHostConfig with optional host hooks (1/3)#4144

Draft
Saadnajmi wants to merge 1 commit into
microsoft:mainfrom
Saadnajmi:feat-host-hooks
Draft

feat(react-native-host): extend RNXHostConfig with optional host hooks (1/3)#4144
Saadnajmi wants to merge 1 commit into
microsoft:mainfrom
Saadnajmi:feat-host-hooks

Conversation

@Saadnajmi
Copy link
Copy Markdown
Contributor

@Saadnajmi Saadnajmi commented May 10, 2026

Description

Adds three optional lifecycle hooks to RNXHostConfig. All three are opt-in via respondsToSelector: — consumers that don't implement them see no behavior change.

host:didLoadInstanceWithError: and hostWillUnloadInstance:

ReactNativeHost subscribes to RCTJavaScriptDidLoadNotification, RCTJavaScriptDidFailToLoadNotification, and RCTBridgeWillBeInvalidatedNotification when the config implements the
corresponding selectors, and forwards. -dealloc removes the observers.

Works in both bridge and bridgeless modes — the same notifications fire in both.

host:didInitializeRuntime: (Objective-C++ only)

Fires inside the bridgeless RCTHost runtime-initialization lambda, after host bindings install (TurboModules, native logger, native component registry) but before the user JS
bundle loads. Useful for evaluating pre-user JS on the runtime — e.g., bundles that must be available before app code runs via runtime.evaluateJavaScript.

Bridgeless mode only. Wired via an internal _RNXForwardingRCTHostDelegate passed as RCTHost's hostDelegate (was nil). Retained as an ivar because RCTHost stores host delegates
weakly. C++-typed (jsi::Runtime &), so only visible to Objective-C++ consumers.

Test plan

Tested internally

Adds three optional lifecycle hooks to RNXHostConfig. All three are
opt-in; consumers that don't implement them see no behavior change.

* host:didLoadInstanceWithError: and hostWillUnloadInstance:
  ReactNativeHost subscribes to RCTJavaScriptDidLoad,
  RCTJavaScriptDidFailToLoad, and RCTBridgeWillBeInvalidated
  notifications and forwards to the config when the corresponding
  selectors are implemented. dealloc removes observers.

* host:didInitializeRuntime: (Objective-C++ only) fires inside the
  bridgeless runtime-init lambda, after host bindings install but
  before the user JS bundle loads. Useful for loading pre-user JS
  (e.g. platform bundles) via runtime.evaluateJavaScript before the
  app bundle runs. Wired via an internal _RNXForwardingRCTHostDelegate
  passed as RCTHost's hostDelegate (was nil); retained as an ivar
  because RCTHost stores host delegates weakly.

/// Called after the JS instance has finished loading. ``error`` is ``nil``
/// on success.
- (void)host:(ReactNativeHost *)host didLoadInstanceWithError:(nullable NSError *)error
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little iffy with the optional error parameter.. the signature makes it feel like there is always an error despite the comment. Alternative is to also expose failedtoLoadInstanceWithError. Thoughts?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be - (void)didLoadInstanceWithHost:(ReactNativeHost *)host error: (nullable NSError **)error, where the user passes a ref to an NSError if they're interested.

@Saadnajmi Saadnajmi changed the title feat(react-native-host): extend RNXHostConfig with optional host hooks feat(react-native-host): extend RNXHostConfig with optional host hooks (1/3) May 11, 2026
@end
#endif // USE_CODEGEN_PROVIDER

#if USE_BRIDGELESS
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this block be in a private header instead?

Comment on lines +140 to +158
if ([config respondsToSelector:@selector(host:didLoadInstanceWithError:)]) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(_rnxInstanceDidLoad:)
name:RCTJavaScriptDidLoadNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(_rnxInstanceDidFailToLoad:)
name:RCTJavaScriptDidFailToLoadNotification
object:nil];
}
if ([config respondsToSelector:@selector(hostWillUnloadInstance:)]) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(_rnxInstanceWillUnload:)
name:RCTBridgeWillBeInvalidatedNotification
object:nil];
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all of this be contained within _RNXForwardingRCTHostDelegate?

/// Called when the instance is about to be unloaded.
- (void)hostWillUnloadInstance:(ReactNativeHost *)host;

#ifdef __cplusplus
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this allowed? Won't we end up with two definitions of this protocol?

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