Skip to content

Commit

Permalink
Fix: Reloading React Native after a destroy shows a "blank screen" (f…
Browse files Browse the repository at this point in the history
…acebook#38999)

Summary:
Pull Request resolved: facebook#38999

After React Native tears down, a RedBox can appear, prompting the user to reload.

**Problem:** After React Native reloads, the React Native screen wouldn't show up.

**Cause:** ReactContext.onHostResume() wasn't executed.

Why:
- React Native teardown moves the React manager into the **onHostDestroy()** state.
- During initialization, React Native only calls ReactContext.onHostResume(), if the React manager was *already* in the **onHostResume()** state.

https://www.internalfb.com/code/fbsource/[f82938c7cc9a0ee722c85c33d1027f326049d37c]/xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridgeless/ReactHost.java?lines=924-925

**Question:** Why does React Native only call ReactContext.onHostResume(), **if the React manager was already in the onHostResume() state?**

This ensures that the application only calls ReactContext.onHostResume() state, **when the user navigates to their *first* React Native screen** (both with and without preload).

(React Native only puts the React manager into the onHostResume() state (from other states), when the user navigates to their first React Native screen).

## The fix
If we're initializing React Native during a reload, just always call ReactContext.onHostResume().

If React Native is reloading, it seems reasonable to assume that:
1. We must have navigated to a React Native screen in the past, or
2. We must be on a React Native screen.

Changelog: [Internal]

Differential Revision: D48076895

fbshipit-source-id: d0640c3b429c205b3f2955c2426c0e15c0ea5c3a
  • Loading branch information
RSNara authored and facebook-github-bot committed Aug 14, 2023
1 parent b5e936e commit f446dca
Showing 1 changed file with 36 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -908,29 +908,51 @@ private Task<ReactInstance> newGetOrCreateReactInstanceTask() {
});

class Result {
final ReactInstance mInstance;
final ReactContext mContext;

Result(ReactInstance instance, ReactContext context) {
mInstance = instance;
mContext = context;
}
final ReactInstance mInstance = instance;
final ReactContext mContext = reactContext;
final boolean mIsReloading = mReloadTask != null;
}

return new Result(instance, reactContext);
return new Result();
},
mBGExecutor)
.onSuccess(
task -> {
ReactInstance reactInstance = task.getResult().mInstance;
ReactContext reactContext = task.getResult().mContext;
final ReactInstance reactInstance = task.getResult().mInstance;
final ReactContext reactContext = task.getResult().mContext;
final boolean isReloading = task.getResult().mIsReloading;

/**
* Call ReactContext.onHostResume() only when already in the resumed state which
* aligns with the bridge https://fburl.com/diffusion/2qhxmudv.
* ReactContext.onHostResume() should only be called when the user navigates to
* the first React Native screen.
*
* <p>The application puts the React manager in a resumed state, when the user
* navigates to a React Native screen.
*
* <p>Two cases: (1) If React Native init happens when the user navigates to a
* React Native screen, the React manager will get resumed on init start, so
* ReactContext.onHostResume() will be executed here. (2) If React Native init
* happens before the user navigates to a React Native screen (i.e: React Native
* is preloaded), the React manager won't be in a resumed state during init. So
* ReactContext.onHostResume() won't be executed here. But, when the user
* navigates to their first React Native screen, the application will call
* ReactHost.onHostResume(). That will call ReactContext.onHostResume().
*
* <p>During reloads, just always call ReactContext.onHostResume(). If React
* Native is reloading, it seems reasonable to assume that: (1) We must have
* navigated to a React Native screen in the past, or (2) We must be on a React
* Native screen.
*/
mReactLifecycleStateManager.resumeReactContextIfHostResumed(
reactContext, mActivity.get());
if (isReloading) {
mReactLifecycleStateManager.moveToOnHostResume(reactContext, mActivity.get());
} else {
/**
* Call ReactContext.onHostResume() only when already in the resumed state
* which aligns with the bridge https://fburl.com/diffusion/2qhxmudv.
*/
mReactLifecycleStateManager.resumeReactContextIfHostResumed(
reactContext, mActivity.get());
}

ReactInstanceEventListener[] listeners =
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
Expand Down

0 comments on commit f446dca

Please sign in to comment.