Skip to content

Commit bfab947

Browse files
committed
Expose ability to unmount an application once ran
1 parent 3c5a6fe commit bfab947

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-7
lines changed

packages/react-native-web/src/exports/AppRegistry/__tests__/index-test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,36 @@ describe.each([['concurrent'], ['legacy']])('AppRegistry', (mode) => {
3838
expect(callback).toHaveBeenCalledTimes(1);
3939
});
4040

41+
test('unmount ran application', () => {
42+
const setMountedState = jest.fn();
43+
const MountedStateComponent = () => {
44+
React.useEffect(() => {
45+
setMountedState(true);
46+
return () => {
47+
setMountedState(false);
48+
};
49+
}, []);
50+
return <NoopComponent />;
51+
};
52+
53+
AppRegistry.registerComponent('App', () => MountedStateComponent);
54+
let application;
55+
act(() => {
56+
application = AppRegistry.runApplication('App', {
57+
initialProps: {},
58+
rootTag,
59+
mode
60+
});
61+
});
62+
expect(setMountedState).toHaveBeenCalledTimes(1);
63+
expect(setMountedState).toHaveBeenLastCalledWith(true);
64+
act(() => {
65+
application.unmount();
66+
});
67+
expect(setMountedState).toHaveBeenCalledTimes(2);
68+
expect(setMountedState).toHaveBeenLastCalledWith(false);
69+
});
70+
4171
test('styles roots in different documents', () => {
4272
AppRegistry.registerComponent('App', () => NoopComponent);
4373
act(() => {

packages/react-native-web/src/exports/AppRegistry/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { ComponentType, Node } from 'react';
1313
import invariant from 'fbjs/lib/invariant';
1414
import unmountComponentAtNode from '../unmountComponentAtNode';
1515
import renderApplication, { getApplication } from './renderApplication';
16+
import type { Application } from './renderApplication';
1617

1718
type AppParams = Object;
1819
type Runnable = {|
@@ -75,7 +76,7 @@ export default class AppRegistry {
7576
appParameters ? appParameters.initialProps : emptyObject,
7677
wrapperComponentProvider && wrapperComponentProvider(appParameters)
7778
),
78-
run: (appParameters) =>
79+
run: (appParameters): Application =>
7980
renderApplication(
8081
componentProviderInstrumentationHook(componentProvider),
8182
wrapperComponentProvider && wrapperComponentProvider(appParameters),
@@ -108,7 +109,7 @@ export default class AppRegistry {
108109
return appKey;
109110
}
110111
111-
static runApplication(appKey: string, appParameters: Object): void {
112+
static runApplication(appKey: string, appParameters: Object): Application {
112113
const isDevelopment =
113114
process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test';
114115
if (isDevelopment) {
@@ -129,7 +130,7 @@ export default class AppRegistry {
129130
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
130131
);
131132

132-
runnables[appKey].run(appParameters);
133+
return runnables[appKey].run(appParameters);
133134
}
134135

135136
static setComponentProviderInstrumentationHook(

packages/react-native-web/src/exports/AppRegistry/renderApplication.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import renderLegacy, { hydrateLegacy, render, hydrate } from '../render';
1616
import StyleSheet from '../StyleSheet';
1717
import React from 'react';
1818

19+
export type Application = {
20+
unmount: () => void
21+
};
22+
1923
export default function renderApplication<Props: Object>(
2024
RootComponent: ComponentType<Props>,
2125
WrapperComponent?: ?ComponentType<*>,
@@ -26,7 +30,7 @@ export default function renderApplication<Props: Object>(
2630
mode: 'concurrent' | 'legacy',
2731
rootTag: any
2832
}
29-
) {
33+
): Application {
3034
const { hydrate: shouldHydrate, initialProps, mode, rootTag } = options;
3135
const renderFn = shouldHydrate
3236
? mode === 'concurrent'
@@ -38,7 +42,7 @@ export default function renderApplication<Props: Object>(
3842

3943
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
4044

41-
renderFn(
45+
return renderFn(
4246
<AppContainer
4347
WrapperComponent={WrapperComponent}
4448
ref={callback}

packages/react-native-web/src/exports/render/index.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
hydrateRoot as domHydrateRoot
1717
} from 'react-dom/client';
1818

19+
import unmountComponentAtNode from '../unmountComponentAtNode';
1920
import { createSheet } from '../StyleSheet/dom';
2021

2122
export function hydrate(element, root) {
@@ -32,10 +33,20 @@ export function render(element, root) {
3233

3334
export function hydrateLegacy(element, root, callback) {
3435
createSheet(root);
35-
return domLegacyHydrate(element, root, callback);
36+
domLegacyHydrate(element, root, callback);
37+
return {
38+
unmount: function () {
39+
return unmountComponentAtNode(root);
40+
}
41+
};
3642
}
3743

3844
export default function renderLegacy(element, root, callback) {
3945
createSheet(root);
40-
return domLegacyRender(element, root, callback);
46+
domLegacyRender(element, root, callback);
47+
return {
48+
unmount: function () {
49+
return unmountComponentAtNode(root);
50+
}
51+
};
4152
}

0 commit comments

Comments
 (0)