[change] React 18 support

* Support React 18 concurrency and constraints.
* Add new render / hydrate functions.
* Remove uses of findNodeHandle.
* Expose ability to unmount an application once ran.

Fix #1529
Close #2330
This commit is contained in:
Eddie Kimmel
2022-08-26 11:19:42 -07:00
committed by Nicolas Gallagher
parent 3a024ee308
commit 9868738604
35 changed files with 10718 additions and 6932 deletions

View File

@@ -2,7 +2,7 @@
"settings": {
"react": {
"pragma": "React",
"version": "17.0",
"version": "18.0",
"flowVersion": "0.148.0" // Flow version
}
},

16800
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -34,7 +34,7 @@
"@babel/preset-env": "^7.18.6",
"@babel/preset-flow": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@testing-library/react": "^12.1.5",
"@testing-library/react": "^13.3.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^28.1.2",
"babel-plugin-add-module-exports": "^1.0.4",

View File

@@ -71,4 +71,7 @@ const tests = {
}))
};
ReactDOM.render(<App tests={tests} />, document.querySelector('.root'));
const root = document.querySelector('.root');
const element = <App tests={tests} />;
ReactDOM.createRoot(root).render(element);

View File

@@ -69,6 +69,10 @@ If the client should hydrate server-rendered HTML.
The initial props passed to the root component.
{% endcall %}
{% call macro.prop('mode', '"concurrent" | "legacy"') %}
Default is 'concurrent'. Setting to 'legacy' will make the app will behave as if its running React 17.
{% endcall %}
{% call macro.prop('rootTag', 'HTMLElement') %}
The native element into which the application is rendered.
{% endcall %}

View File

@@ -10,8 +10,8 @@
"dependencies": {
"babel-plugin-react-native-web": "0.18.12",
"next": "^12.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-native-web": "0.18.12"
},
"devDependencies": {

View File

@@ -25,18 +25,18 @@ export default function AppStatePage() {
const iframeRootTag = document.createElement('div');
iframeRootTag.id = 'iframe-root';
iframeBody.appendChild(iframeRootTag);
AppRegistry.runApplication('App', { rootTag: iframeRootTag });
const app1 = AppRegistry.runApplication('App', { rootTag: iframeRootTag });
const shadowElement = shadowRef.current;
const shadowRoot = shadowElement.attachShadow({ mode: 'open' });
const shadowRootTag = document.createElement('div');
shadowRootTag.id = 'shadow-root';
shadowRoot.appendChild(shadowRootTag);
AppRegistry.runApplication('App', { rootTag: shadowRootTag });
const app2 = AppRegistry.runApplication('App', { rootTag: shadowRootTag });
return () => {
AppRegistry.unmountApplicationComponentAtRootTag(iframeRootTag);
AppRegistry.unmountApplicationComponentAtRootTag(shadowRootTag);
app1.unmount();
app2.unmount();
};
}, []);

View File

@@ -31,8 +31,8 @@
"styleq": "^0.1.2"
},
"peerDependencies": {
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0"
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"author": "Nicolas Gallagher",
"license": "MIT",

View File

@@ -7,9 +7,8 @@
import ActivityIndicator from '..';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
describe('components/ActivityIndicator', () => {
describe('prop "accessibilityLabel"', () => {

View File

@@ -21,30 +21,40 @@ type Props = {
const RootTagContext: React.Context<any> = React.createContext(null);
export default function AppContainer(props: Props): React.Node {
const { children, WrapperComponent } = props;
const AppContainer: React.AbstractComponent<Props> = React.forwardRef(
(props: Props, forwardedRef?: React.Ref<any>) => {
const { children, WrapperComponent } = props;
let innerView = (
<View
children={children}
key={1}
pointerEvents="box-none"
style={styles.appContainer}
/>
);
let innerView = (
<View
children={children}
key={1}
pointerEvents="box-none"
style={styles.appContainer}
/>
);
if (WrapperComponent) {
innerView = <WrapperComponent>{innerView}</WrapperComponent>;
if (WrapperComponent) {
innerView = <WrapperComponent>{innerView}</WrapperComponent>;
}
return (
<RootTagContext.Provider value={props.rootTag}>
<View
pointerEvents="box-none"
ref={forwardedRef}
style={styles.appContainer}
>
{innerView}
</View>
</RootTagContext.Provider>
);
}
);
return (
<RootTagContext.Provider value={props.rootTag}>
<View pointerEvents="box-none" style={styles.appContainer}>
{innerView}
</View>
</RootTagContext.Provider>
);
}
AppContainer.displayName = 'AppContainer';
export default AppContainer;
const styles = StyleSheet.create({
appContainer: {

View File

@@ -7,10 +7,10 @@
import AppRegistry from '..';
import React from 'react';
import { act } from '@testing-library/react';
const NoopComponent = () => React.createElement('div');
describe('AppRegistry', () => {
describe.each([['concurrent'], ['legacy']])('AppRegistry', (mode) => {
describe('runApplication', () => {
let rootTag;
@@ -27,18 +27,52 @@ describe('AppRegistry', () => {
test('callback after render', () => {
const callback = jest.fn();
AppRegistry.registerComponent('App', () => NoopComponent);
AppRegistry.runApplication('App', {
initialProps: {},
rootTag,
callback
act(() => {
AppRegistry.runApplication('App', {
initialProps: {},
rootTag,
callback,
mode
});
});
expect(callback).toHaveBeenCalledTimes(1);
});
test('unmount ran application', () => {
const setMountedState = jest.fn();
const MountedStateComponent = () => {
React.useEffect(() => {
setMountedState(true);
return () => {
setMountedState(false);
};
}, []);
return <NoopComponent />;
};
AppRegistry.registerComponent('App', () => MountedStateComponent);
let application;
act(() => {
application = AppRegistry.runApplication('App', {
initialProps: {},
rootTag,
mode
});
});
expect(setMountedState).toHaveBeenCalledTimes(1);
expect(setMountedState).toHaveBeenLastCalledWith(true);
act(() => {
application.unmount();
});
expect(setMountedState).toHaveBeenCalledTimes(2);
expect(setMountedState).toHaveBeenLastCalledWith(false);
});
test('styles roots in different documents', () => {
AppRegistry.registerComponent('App', () => NoopComponent);
AppRegistry.runApplication('App', { initialProps: {}, rootTag });
act(() => {
AppRegistry.runApplication('App', { initialProps: {}, rootTag, mode });
});
// Create iframe context
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
@@ -49,9 +83,12 @@ describe('AppRegistry', () => {
// Run in iframe
AppRegistry.registerComponent('App', () => NoopComponent);
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: iframeRootTag
act(() => {
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: iframeRootTag,
mode
});
});
const iframedoc = iframeRootTag.ownerDocument;

View File

@@ -8,6 +8,7 @@
* @flow
*/
import type { Application } from './renderApplication';
import type { ComponentType, Node } from 'react';
import invariant from 'fbjs/lib/invariant';
@@ -75,7 +76,7 @@ export default class AppRegistry {
appParameters ? appParameters.initialProps : emptyObject,
wrapperComponentProvider && wrapperComponentProvider(appParameters)
),
run: (appParameters) =>
run: (appParameters): Application =>
renderApplication(
componentProviderInstrumentationHook(componentProvider),
wrapperComponentProvider && wrapperComponentProvider(appParameters),
@@ -83,6 +84,7 @@ export default class AppRegistry {
{
hydrate: appParameters.hydrate || false,
initialProps: appParameters.initialProps || emptyObject,
mode: appParameters.mode || 'concurrent',
rootTag: appParameters.rootTag
}
)
@@ -107,7 +109,7 @@ export default class AppRegistry {
return appKey;
}
static runApplication(appKey: string, appParameters: Object): void {
static runApplication(appKey: string, appParameters: Object): Application {
const isDevelopment =
process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test';
if (isDevelopment) {
@@ -128,7 +130,7 @@ export default class AppRegistry {
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
);
runnables[appKey].run(appParameters);
return runnables[appKey].run(appParameters);
}
static setComponentProviderInstrumentationHook(

View File

@@ -12,10 +12,14 @@ import type { ComponentType, Node } from 'react';
import AppContainer from './AppContainer';
import invariant from 'fbjs/lib/invariant';
import render, { hydrate } from '../render';
import renderLegacy, { hydrateLegacy, render, hydrate } from '../render';
import StyleSheet from '../StyleSheet';
import React from 'react';
export type Application = {
unmount: () => void
};
export default function renderApplication<Props: Object>(
RootComponent: ComponentType<Props>,
WrapperComponent?: ?ComponentType<*>,
@@ -23,20 +27,30 @@ export default function renderApplication<Props: Object>(
options: {
hydrate: boolean,
initialProps: Props,
mode: 'concurrent' | 'legacy',
rootTag: any
}
) {
const { hydrate: shouldHydrate, initialProps, rootTag } = options;
const renderFn = shouldHydrate ? hydrate : render;
): Application {
const { hydrate: shouldHydrate, initialProps, mode, rootTag } = options;
const renderFn = shouldHydrate
? mode === 'concurrent'
? hydrate
: hydrateLegacy
: mode === 'concurrent'
? render
: renderLegacy;
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
renderFn(
<AppContainer WrapperComponent={WrapperComponent} rootTag={rootTag}>
return renderFn(
<AppContainer
WrapperComponent={WrapperComponent}
ref={callback}
rootTag={rootTag}
>
<RootComponent {...initialProps} />
</AppContainer>,
rootTag,
callback
rootTag
);
}

View File

@@ -1,8 +1,7 @@
import Button from '..';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
describe('components/Button', () => {
test('prop "accessibilityLabel"', () => {

View File

@@ -7,9 +7,8 @@
import CheckBox from '../';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
function findCheckbox(container) {
return container.firstChild.querySelector('input');

View File

@@ -7,13 +7,12 @@
/* eslint-disable react/jsx-no-bind */
import { act } from 'react-dom/test-utils';
import * as AssetRegistry from '../../../modules/AssetRegistry';
import Image from '../';
import ImageLoader, { ImageUriCache } from '../../../modules/ImageLoader';
import PixelRatio from '../../PixelRatio';
import React from 'react';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
const originalImage = window.Image;

View File

@@ -7,9 +7,8 @@
import React from 'react';
import Pressable from '../';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
describe('components/Pressable', () => {
test('default', () => {

View File

@@ -1,9 +1,8 @@
import React from 'react';
import ScrollView from '../';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { findDOMNode } from 'react-dom';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
describe('components/ScrollView', () => {
describe('prop "centerContent"', () => {

View File

@@ -9,9 +9,8 @@
import React from 'react';
import Text from '../';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
describe('components/Text', () => {
test('default', () => {

View File

@@ -7,9 +7,8 @@
import React from 'react';
import TextInput from '..';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
function findInput(container) {
return container.querySelector('input');

View File

@@ -13,7 +13,6 @@ import type { Node } from 'React';
import AccessibilityUtil from '../../modules/AccessibilityUtil';
import BoundingDimensions from './BoundingDimensions';
import findNodeHandle from '../findNodeHandle';
import normalizeColor from 'normalize-css-color';
import Position from './Position';
import React from 'react';
@@ -73,6 +72,11 @@ const extractSingleTouch = (nativeEvent) => {
* return merge(this.touchableGetInitialState(), yourComponentState);
* }
*
* - Add a method to get your touchable component's node.
* getTouchableNode: function() {
* return this.touchableRef.current
* }
*
* - Choose the rendered component who's touches should start the interactive
* sequence. On that rendered node, forward all `Touchable` responder
* handlers. You can choose any rendered node you like. Choose a node whose
@@ -81,6 +85,7 @@ const extractSingleTouch = (nativeEvent) => {
* // In render function:
* return (
* <View
* ref={this.touchableRef}
* onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
* onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
* onResponderGrant={this.touchableHandleResponderGrant}
@@ -366,8 +371,8 @@ const LONG_PRESS_ALLOWED_MOVEMENT = 10;
const TouchableMixin = {
// HACK (part 1): basic support for touchable interactions using a keyboard
componentDidMount: function () {
this._touchableNode = findNodeHandle(this);
if (this._touchableNode && this._touchableNode.addEventListener) {
const touchableNode = this.getTouchableNode && this.getTouchableNode();
if (touchableNode && touchableNode.addEventListener) {
this._touchableBlurListener = (e) => {
if (this._isTouchableKeyboardActive) {
if (
@@ -379,7 +384,7 @@ const TouchableMixin = {
this._isTouchableKeyboardActive = false;
}
};
this._touchableNode.addEventListener('blur', this._touchableBlurListener);
touchableNode.addEventListener('blur', this._touchableBlurListener);
}
},
@@ -387,11 +392,9 @@ const TouchableMixin = {
* Clear all timeouts on unmount
*/
componentWillUnmount: function () {
if (this._touchableNode && this._touchableNode.addEventListener) {
this._touchableNode.removeEventListener(
'blur',
this._touchableBlurListener
);
const touchableNode = this.getTouchableNode && this.getTouchableNode();
if (touchableNode && touchableNode.addEventListener) {
touchableNode.removeEventListener('blur', this._touchableBlurListener);
}
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout);
@@ -399,7 +402,6 @@ const TouchableMixin = {
// Clear DOM nodes
this.pressInLocation = null;
this.state.touchable.responderID = null;
this._touchableNode = null;
},
/**

View File

@@ -8,9 +8,8 @@
import React from 'react';
import View from '../';
import StyleSheet from '../../StyleSheet';
import { act } from 'react-dom/test-utils';
import { createEventTarget } from 'dom-event-testing-library';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
describe('components/View', () => {
test('default', () => {

View File

@@ -10,6 +10,10 @@
import { findDOMNode } from 'react-dom';
/**
* @deprecated imperatively finding the DOM element of a react component has been deprecated in React 18.
* You should use ref properties on the component instead.
*/
const findNodeHandle = (component) => {
let node;

View File

@@ -7,15 +7,46 @@
* @noflow
*/
import { hydrate as domHydrate, render as domRender } from 'react-dom';
import {
hydrate as domLegacyHydrate,
render as domLegacyRender
} from 'react-dom';
import {
createRoot as domCreateRoot,
hydrateRoot as domHydrateRoot
} from 'react-dom/client';
import unmountComponentAtNode from '../unmountComponentAtNode';
import { createSheet } from '../StyleSheet/dom';
export function hydrate(element, root, callback) {
export function hydrate(element, root) {
createSheet(root);
return domHydrate(element, root, callback);
return domHydrateRoot(root, element);
}
export default function render(element, root, callback) {
export function render(element, root) {
createSheet(root);
return domRender(element, root, callback);
const reactRoot = domCreateRoot(root);
reactRoot.render(element);
return reactRoot;
}
export function hydrateLegacy(element, root, callback) {
createSheet(root);
domLegacyHydrate(element, root, callback);
return {
unmount: function () {
return unmountComponentAtNode(root);
}
};
}
export default function renderLegacy(element, root, callback) {
createSheet(root);
domLegacyRender(element, root, callback);
return {
unmount: function () {
return unmountComponentAtNode(root);
}
};
}

View File

@@ -9,7 +9,6 @@
*/
import Dimensions from '../../exports/Dimensions';
import findNodeHandle from '../../exports/findNodeHandle';
import invariant from 'fbjs/lib/invariant';
import Platform from '../../exports/Platform';
import TextInputState from '../TextInputState';
@@ -347,17 +346,6 @@ const ScrollResponderMixin = {
return isAnimating;
},
/**
* Returns the node that represents native view that can be scrolled.
* Components can pass what node to use by defining a `getScrollableNode`
* function otherwise `this` is used.
*/
scrollResponderGetScrollableNode: function (): any {
return this.getScrollableNode
? this.getScrollableNode()
: findNodeHandle(this);
},
/**
* A helper function to scroll to a specific point in the scrollview.
* This is currently used to help focus on child textviews, but can also
@@ -381,7 +369,7 @@ const ScrollResponderMixin = {
} else {
({ x, y, animated } = x || emptyObject);
}
const node = this.scrollResponderGetScrollableNode();
const node = this.getScrollableNode();
const left = x || 0;
const top = y || 0;
if (typeof node.scroll === 'function') {
@@ -437,7 +425,7 @@ const ScrollResponderMixin = {
this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
UIManager.measureLayout(
nodeHandle,
findNodeHandle(this.getInnerViewNode()),
this.getInnerViewNode(),
this.scrollResponderTextInputFocusError,
this.scrollResponderInputMeasureAndScrollToKeyboard
);

View File

@@ -8,53 +8,11 @@
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as ReactDOMServer from 'react-dom/server';
import { act } from 'react-dom/test-utils';
import { act, render } from '@testing-library/react';
import { createEventTarget } from 'dom-event-testing-library';
import createEventHandle from '..';
function createRoot(rootNode) {
return {
render(element) {
ReactDOM.render(element, rootNode);
}
};
}
describe('create-event-handle', () => {
let root;
let rootNode;
beforeEach(() => {
rootNode = document.createElement('div');
document.body.appendChild(rootNode);
root = createRoot(rootNode);
});
afterEach(() => {
root.render(null);
document.body.removeChild(rootNode);
rootNode = null;
root = null;
});
test('can render correctly using ReactDOMServer', () => {
const listener = jest.fn();
const targetRef = React.createRef();
const addClickListener = createEventHandle('click');
function Component() {
React.useEffect(() => {
return addClickListener(targetRef.current, listener);
});
return <div ref={targetRef} />;
}
const output = ReactDOMServer.renderToString(<Component />);
expect(output).toBe('<div data-reactroot=""></div>');
});
describe('createEventTarget()', () => {
test('event dispatched on target', () => {
const listener = jest.fn();
@@ -68,9 +26,7 @@ describe('create-event-handle', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
@@ -113,9 +69,7 @@ describe('create-event-handle', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const parent = createEventTarget(parentRef.current);
@@ -164,9 +118,7 @@ describe('create-event-handle', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -196,9 +148,7 @@ describe('create-event-handle', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const text = createEventTarget(childRef.current.firstChild);
@@ -221,9 +171,8 @@ describe('create-event-handle', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component target={document} />);
});
render(<Component target={document} />);
const target = createEventTarget(targetRef.current);
act(() => {
target.click();
@@ -244,9 +193,8 @@ describe('create-event-handle', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component target={window} />);
});
render(<Component target={window} />);
const target = createEventTarget(targetRef.current);
act(() => {
target.click();
@@ -267,9 +215,7 @@ describe('create-event-handle', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component />);
});
render(<Component />);
act(() => {
const event = new CustomEvent('magic-event', { bubbles: true });
@@ -312,9 +258,7 @@ describe('create-event-handle', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -371,9 +315,7 @@ describe('create-event-handle', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -416,9 +358,7 @@ describe('create-event-handle', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -447,9 +387,7 @@ describe('create-event-handle', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
*/
import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import createEventHandle from '..';
describe('create-event-handle', () => {
test('can render correctly using ReactDOMServer', () => {
const listener = jest.fn();
const targetRef = React.createRef();
const addClickListener = createEventHandle('click');
function Component() {
React.useEffect(() => {
return addClickListener(targetRef.current, listener);
});
return <div ref={targetRef} />;
}
const output = ReactDOMServer.renderToString(<Component />);
expect(output).toBe('<div></div>');
});
});

View File

@@ -8,36 +8,11 @@
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import { act, render } from '@testing-library/react';
import { createEventTarget } from 'dom-event-testing-library';
import useEvent from '..';
function createRoot(rootNode) {
return {
render(element) {
ReactDOM.render(element, rootNode);
}
};
}
describe('use-event', () => {
let root;
let rootNode;
beforeEach(() => {
rootNode = document.createElement('div');
document.body.appendChild(rootNode);
root = createRoot(rootNode);
});
afterEach(() => {
root.render(null);
document.body.removeChild(rootNode);
rootNode = null;
root = null;
});
describe('setListener()', () => {
test('event dispatched on target', () => {
const listener = jest.fn();
@@ -51,9 +26,7 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
@@ -85,9 +58,7 @@ describe('use-event', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const parent = createEventTarget(parentRef.current);
@@ -125,9 +96,7 @@ describe('use-event', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -157,9 +126,7 @@ describe('use-event', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const text = createEventTarget(childRef.current.firstChild);
@@ -182,9 +149,8 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component target={document} />);
});
render(<Component target={document} />);
const target = createEventTarget(targetRef.current);
act(() => {
target.click();
@@ -205,9 +171,8 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component target={window} />);
});
render(<Component target={window} />);
const target = createEventTarget(targetRef.current);
act(() => {
target.click();
@@ -229,18 +194,16 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component onClick={listener} />);
});
const { rerender } = render(<Component onClick={listener} />);
const target = createEventTarget(targetRef.current);
act(() => {
target.click();
});
expect(listener).toBeCalledTimes(1);
act(() => {
// this should replace the listener
root.render(<Component onClick={listenerAlt} />);
});
rerender(<Component onClick={listenerAlt} />);
act(() => {
target.click();
});
@@ -260,18 +223,17 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component off={false} />);
});
const { unmount } = render(<Component off={false} />);
const target = createEventTarget(targetRef.current);
act(() => {
target.click();
});
expect(listener).toBeCalledTimes(1);
act(() => {
// this should unset the listener
root.render(<Component off={true} />);
});
// this should unset the listener
unmount();
listener.mockClear();
act(() => {
target.click();
@@ -291,9 +253,7 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component />);
});
render(<Component />);
act(() => {
const event = new CustomEvent('magic-event', { bubbles: true });
@@ -335,9 +295,7 @@ describe('use-event', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -391,9 +349,7 @@ describe('use-event', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -425,10 +381,8 @@ describe('use-event', () => {
return <div />;
}
act(() => {
root.render(<Component />);
root.render(null);
});
const { unmount } = render(<Component />);
unmount();
const target = createEventTarget(document);
@@ -462,9 +416,7 @@ describe('use-event', () => {
);
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const child = createEventTarget(childRef.current);
@@ -493,9 +445,7 @@ describe('use-event', () => {
return <div ref={targetRef} />;
}
act(() => {
root.render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);

View File

@@ -5,9 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { act } from 'react-dom/test-utils';
import { act, render } from '@testing-library/react';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
describeWithPointerEvent,
clearPointers,
@@ -17,30 +16,12 @@ import {
import useHover from '..';
import { testOnly_resetActiveModality } from '../../modality';
function createRoot(rootNode) {
return {
render(element) {
ReactDOM.render(element, rootNode);
}
};
}
describeWithPointerEvent('useHover', (hasPointerEvents) => {
let root;
let rootNode;
beforeEach(() => {
setPointerEvent(hasPointerEvents);
rootNode = document.createElement('div');
document.body.appendChild(rootNode);
root = createRoot(rootNode);
});
afterEach(() => {
root.render(null);
document.body.removeChild(rootNode);
rootNode = null;
root = null;
testOnly_resetActiveModality();
// make sure all tests reset state machine tracking pointers on the mock surface
clearPointers();
@@ -70,9 +51,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
</div>
);
};
act(() => {
root.render(<Component />);
});
render(<Component />);
};
test('contains the hover gesture', () => {
@@ -111,9 +90,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
});
return <div ref={ref} />;
};
act(() => {
root.render(<Component />);
});
render(<Component />);
};
test('does not call callbacks', () => {
@@ -140,9 +117,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
useHover(ref, { onHoverStart });
return <div ref={ref} />;
};
act(() => {
root.render(<Component />);
});
render(<Component />);
};
test('is called for mouse pointers', () => {
@@ -186,9 +161,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
useHover(ref, { onHoverChange });
return <div ref={ref} />;
};
act(() => {
root.render(<Component />);
});
render(<Component />);
};
test('is called for mouse pointers', () => {
@@ -232,9 +205,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
</div>
);
};
act(() => {
root.render(<Component />);
});
render(<Component />);
};
test('is called for mouse pointers', () => {
@@ -278,9 +249,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
useHover(ref, { onHoverUpdate });
return <div ref={ref} />;
};
act(() => {
root.render(<Component />);
});
render(<Component />);
const target = createEventTarget(ref.current);
act(() => {
@@ -310,9 +279,7 @@ describeWithPointerEvent('useHover', (hasPointerEvents) => {
});
return <div ref={ref} />;
};
act(() => {
root.render(<Component />);
});
render(<Component />);
};
test('callbacks are called each time', () => {

View File

@@ -6,8 +6,7 @@
*/
import * as React from 'react';
import { act } from 'react-dom/test-utils';
import { cleanup, render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
import useMergeRefs from '..';
describe('modules/useMergeRefs', () => {
@@ -16,8 +15,6 @@ describe('modules/useMergeRefs', () => {
return <div ref={mergedRef} {...rest} />;
}
afterEach(cleanup);
test('handles no refs', () => {
act(() => {
render(<TestComponent refs={[]} />);

View File

@@ -5,9 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { act } from 'react-dom/test-utils';
import { act, render } from '@testing-library/react';
import React, { createRef } from 'react';
import ReactDOM from 'react-dom';
import useResponderEvents from '..';
import { getResponderNode, terminateResponder } from '../ResponderSystem';
import {
@@ -19,21 +18,7 @@ import {
} from 'dom-event-testing-library';
describe('useResponderEvents', () => {
let container;
function render(element) {
ReactDOM.render(element, container);
}
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
render(null);
document.body.removeChild(container);
container = null;
// make sure all tests end with the current responder being reset
terminateResponder();
// make sure all tests reset state machine tracking pointers on the mock surface
@@ -52,9 +37,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture
act(() => {
@@ -76,9 +59,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const buttons = [1, 2, 3, 4];
// gesture
@@ -105,9 +86,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const acceptedModifierKeys = ['metaKey', 'shiftKey'];
const ignoredModifierKeys = ['altKey', 'ctrlKey'];
@@ -139,9 +118,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// touch gesture
act(() => {
@@ -186,9 +163,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture
act(() => {
@@ -245,9 +220,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -302,9 +275,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -355,9 +326,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -426,9 +395,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -475,9 +442,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -526,9 +491,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -596,9 +559,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -653,9 +614,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -707,9 +666,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -779,9 +736,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -830,9 +785,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -882,9 +835,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -943,9 +894,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -987,9 +936,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -1038,9 +985,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -1067,9 +1012,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -1134,9 +1077,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
act(() => {
@@ -1181,9 +1122,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & move
act(() => {
@@ -1228,9 +1167,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & end
act(() => {
@@ -1273,9 +1210,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture
act(() => {
@@ -1320,9 +1255,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start & cancel
act(() => {
@@ -1359,9 +1292,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const input = createEventTarget(inputRef.current);
// getSelection is not supported in jest
@@ -1389,9 +1320,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const doc = createEventTarget(document);
// getSelection is not supported in jest
@@ -1437,9 +1366,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const parent = createEventTarget(parentRef.current);
// gesture start & scroll
@@ -1473,9 +1400,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const doc = createEventTarget(document);
// gesture start & scroll
@@ -1510,9 +1435,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const sibling = createEventTarget(siblingRef.current);
// gesture start & scroll
@@ -1541,9 +1464,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const doc = createEventTarget(document);
// gesture start & blur
@@ -1571,9 +1492,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const win = createEventTarget(window);
// gesture start & blur
@@ -1607,9 +1526,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const sibling = createEventTarget(siblingRef.current);
// gesture start & blur
@@ -1640,9 +1557,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// contextmenu sequence includes pointerdown "start"
act(() => {
@@ -1675,9 +1590,7 @@ describe('useResponderEvents', () => {
}
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const doc = createEventTarget(document);
// contextmenu
@@ -1860,9 +1773,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
@@ -2034,9 +1945,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// gesture start
@@ -2199,9 +2108,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const parent = createEventTarget(parentRef.current);
const target = createEventTarget(targetRef.current);
@@ -2387,9 +2294,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const sibling = createEventTarget(siblingRef.current);
// gesture start on target
@@ -2530,9 +2435,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const sibling = createEventTarget(siblingRef.current);
// gesture start and move on target
@@ -2636,9 +2539,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
// first touch
act(() => {
@@ -2773,9 +2674,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
const parent = createEventTarget(parentRef.current);
// first touch
@@ -2876,9 +2775,7 @@ describe('useResponderEvents', () => {
};
// render
act(() => {
render(<Component />);
});
render(<Component />);
const target = createEventTarget(targetRef.current);
act(() => {
target.pointerdown({ pointerType });

View File

@@ -8,21 +8,10 @@
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import { render } from '@testing-library/react';
import useStable from '..';
function createRoot(rootNode) {
return {
render(element) {
ReactDOM.render(element, rootNode);
}
};
}
describe('useStable', () => {
let root;
let rootNode;
let spy = {};
const TestComponent = ({ initialValueCallback }): React.Node => {
@@ -33,59 +22,33 @@ describe('useStable', () => {
beforeEach(() => {
spy = {};
rootNode = document.createElement('div');
document.body.appendChild(rootNode);
root = createRoot(rootNode);
});
afterEach(() => {
root.render(null);
document.body.removeChild(rootNode);
rootNode = null;
root = null;
});
test('correctly sets the initial value', () => {
const initialValueCallback = () => 5;
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
render(<TestComponent initialValueCallback={initialValueCallback} />);
expect(spy.value).toBe(5);
});
test('does not change the value', () => {
let counter = 0;
const initialValueCallback = () => counter++;
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
const { rerender } = render(
<TestComponent initialValueCallback={initialValueCallback} />
);
expect(spy.value).toBe(0);
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
rerender(<TestComponent initialValueCallback={initialValueCallback} />);
expect(spy.value).toBe(0);
});
test('only calls the callback once', () => {
let counter = 0;
const initialValueCallback = () => counter++;
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
const { rerender } = render(
<TestComponent initialValueCallback={initialValueCallback} />
);
expect(counter).toBe(1);
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
rerender(<TestComponent initialValueCallback={initialValueCallback} />);
expect(counter).toBe(1);
});
@@ -98,17 +61,11 @@ describe('useStable', () => {
}
return counter++;
};
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
const { rerender } = render(
<TestComponent initialValueCallback={initialValueCallback} />
);
expect(spy.value).toBe(null);
act(() => {
root.render(
<TestComponent initialValueCallback={initialValueCallback} />
);
});
rerender(<TestComponent initialValueCallback={initialValueCallback} />);
expect(spy.value).toBe(null);
});
});

View File

@@ -12,7 +12,6 @@
import AnimatedValue from './nodes/AnimatedValue';
import NativeAnimatedHelper from './NativeAnimatedHelper';
import findNodeHandle from '../../../exports/findNodeHandle';
import invariant from 'fbjs/lib/invariant';
@@ -58,11 +57,10 @@ export function attachNativeEvent(
// Assume that the event containing `nativeEvent` is always the first argument.
traverse(argMapping[0].nativeEvent, []);
const viewTag = findNodeHandle(viewRef);
if (viewTag != null) {
if (viewRef != null) {
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.addAnimatedEventToView(
viewTag,
viewRef,
eventName,
mapping,
);
@@ -71,10 +69,10 @@ export function attachNativeEvent(
return {
detach() {
if (viewTag != null) {
if (viewRef != null) {
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.removeAnimatedEventFromView(
viewTag,
viewRef,
eventName,
// $FlowFixMe[incompatible-call]
mapping.animatedValueTag,

View File

@@ -14,7 +14,6 @@ import {AnimatedEvent} from '../AnimatedEvent';
import AnimatedNode from './AnimatedNode';
import AnimatedStyle from './AnimatedStyle';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
import findNodeHandle from '../../../../exports/findNodeHandle';
import invariant from 'fbjs/lib/invariant';
@@ -119,9 +118,7 @@ class AnimatedProps extends AnimatedNode {
__connectAnimatedView(): void {
invariant(this.__isNative, 'Expected node to be marked as "native"');
const nativeViewTag: ?number = findNodeHandle(
this._animatedView,
);
const nativeViewTag: ?number = this._animatedView
invariant(
nativeViewTag != null,
'Unable to locate attached view in the native tree',
@@ -134,9 +131,7 @@ class AnimatedProps extends AnimatedNode {
__disconnectAnimatedView(): void {
invariant(this.__isNative, 'Expected node to be marked as "native"');
const nativeViewTag: ?number = findNodeHandle(
this._animatedView,
);
const nativeViewTag: ?number = this._animatedView
invariant(
nativeViewTag != null,
'Unable to locate attached view in the native tree',

View File

@@ -10,7 +10,6 @@
import Batchinator from '../Batchinator';
import FillRateHelper from '../FillRateHelper';
import findNodeHandle from '../../../exports/findNodeHandle';
import RefreshControl from '../../../exports/RefreshControl';
import ScrollView from '../../../exports/ScrollView';
import StyleSheet from '../../../exports/StyleSheet';
@@ -573,7 +572,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
if (this._scrollRef && this._scrollRef.getScrollableNode) {
return this._scrollRef.getScrollableNode();
} else {
return findNodeHandle(this._scrollRef);
return this._scrollRef;
}
}