Fix: React Native reloads after teardown render a "blank screen" (#38999)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/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?**

In short, we want ReactContext.onHostResume() to be delayed until the user navigates to the first React Native screen. Please read the comments in the code to understand why.

## 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]

Reviewed By: mdvacca

Differential Revision: D48076895

fbshipit-source-id: 6794105920ee30d62cebd745256bb5dea805e891
This commit is contained in:
Ramanpreet Nara
2023-08-21 12:43:42 -07:00
committed by Facebook GitHub Bot
parent ecd2feeea0
commit 75c5d74ea2
@@ -909,29 +909,52 @@ public class ReactHostImpl implements ReactHost {
});
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;
final boolean isManagerResumed =
mReactLifecycleStateManager.getLifecycleState() == LifecycleState.RESUMED;
/**
* 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>During init: The application puts the React manager in a resumed state,
* when the user navigates to a React Native screen. Two types of init: (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 here. 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, if the manager isn't resumed, 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 && !isManagerResumed) {
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()];