diff --git a/packages/react-native-web/src/exports/ImageBackground/__tests__/index.js b/packages/react-native-web/src/exports/ImageBackground/__tests__/index.js
index 30a61b4a..eb1e925b 100644
--- a/packages/react-native-web/src/exports/ImageBackground/__tests__/index.js
+++ b/packages/react-native-web/src/exports/ImageBackground/__tests__/index.js
@@ -1,36 +1,39 @@
/* eslint-env jasmine, jest */
-import Image from '../../Image';
import ImageBackground from '..';
import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from '@testing-library/react';
import Text from '../../Text';
+function findImage(container) {
+ return container.firstChild.firstChild;
+}
+
describe('components/ImageBackground', () => {
describe('prop "children"', () => {
- it('render child content', () => {
- const component = shallow(
+ test('render child content', () => {
+ const { getByText } = render(
Hello World!
);
- expect(component.find(Text)).toBeDefined();
+ expect(getByText('Hello World!')).toBeDefined();
});
});
describe('prop "imageStyle"', () => {
- it('sets the style of the underlying Image', () => {
+ test('sets the style of the underlying Image', () => {
const imageStyle = { width: 40, height: 60 };
- const component = shallow();
- expect(component.find(Image).prop('style')).toContain(imageStyle);
+ const { container } = render();
+ expect(findImage(container).getAttribute('style')).toBe('height: 60px; width: 40px;');
});
});
describe('prop "style"', () => {
- it('sets the style of the container View', () => {
+ test('sets the style of the container View', () => {
const style = { margin: 40 };
- const component = shallow();
- expect(component.prop('style')).toEqual(style);
+ const { container } = render();
+ expect(container.firstChild.getAttribute('style')).toEqual('margin: 40px 40px 40px 40px;');
});
});
});
diff --git a/packages/react-native-web/src/exports/ImageBackground/index.js b/packages/react-native-web/src/exports/ImageBackground/index.js
index 22022bbc..9b4f8e1d 100644
--- a/packages/react-native-web/src/exports/ImageBackground/index.js
+++ b/packages/react-native-web/src/exports/ImageBackground/index.js
@@ -10,11 +10,11 @@
import type { ImageProps } from '../Image';
import type { ViewProps } from '../View';
-import ensureComponentIsNative from '../../modules/ensureComponentIsNative';
+import setAndForwardRef from '../../modules/setAndForwardRef';
import Image from '../Image';
import StyleSheet from '../StyleSheet';
import View from '../View';
-import React from 'react';
+import React, { forwardRef, useImperativeHandle, useRef } from 'react';
type ImageBackgroundProps = {
...ImageProps,
@@ -28,52 +28,58 @@ const emptyObject = {};
/**
* Very simple drop-in replacement for which supports nesting views.
*/
-class ImageBackground extends React.Component {
- setNativeProps(props: Object) {
- // Work-around flow
- const viewRef = this._viewRef;
- if (viewRef) {
- ensureComponentIsNative(viewRef);
- viewRef.setNativeProps(props);
+const ImageBackground = forwardRef((props, ref) => {
+ const { children, style = emptyObject, imageStyle, imageRef, ...rest } = props;
+ const { height, width } = StyleSheet.flatten(style);
+
+ const containerRef = useRef(null);
+
+ useImperativeHandle(
+ ref,
+ () => ({
+ setNativeProps(props) {
+ if (containerRef.current != null) {
+ containerRef.current.setNativeProps(props);
+ }
+ }
+ }),
+ [containerRef]
+ );
+
+ const setRef = setAndForwardRef({
+ getForwardedRef: () => ref,
+ setLocalRef: c => {
+ containerRef.current = c;
}
- }
+ });
- _viewRef: ?View = null;
+ return (
+
+ overwrites width and height styles
+ // (which is not quite correct), and these styles conflict with explicitly set styles
+ // of and with our internal layout model here.
+ // So, we have to proxy/reapply these styles explicitly for actual component.
+ // This workaround should be removed after implementing proper support of
+ // intrinsic content size of the .
+ width,
+ height,
+ zIndex: -1
+ },
+ imageStyle
+ ]}
+ />
+ {children}
+
+ );
+});
- _captureRef = (ref: View) => {
- this._viewRef = ref;
- };
-
- render() {
- const { children, style = emptyObject, imageStyle, imageRef, ...props } = this.props;
- const { height, width } = StyleSheet.flatten(style);
-
- return (
-
- overwrites width and height styles
- // (which is not quite correct), and these styles conflict with explicitly set styles
- // of and with our internal layout model here.
- // So, we have to proxy/reapply these styles explicitly for actual component.
- // This workaround should be removed after implementing proper support of
- // intrinsic content size of the .
- width,
- height,
- zIndex: -1
- },
- imageStyle
- ]}
- />
- {children}
-
- );
- }
-}
+ImageBackground.displayName = 'ImageBackground';
export default ImageBackground;
diff --git a/packages/react-native-web/src/modules/setAndForwardRef/index.js b/packages/react-native-web/src/modules/setAndForwardRef/index.js
new file mode 100644
index 00000000..9f006482
--- /dev/null
+++ b/packages/react-native-web/src/modules/setAndForwardRef/index.js
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import * as React from 'react';
+
+type Args = $ReadOnly<{|
+ getForwardedRef: () => ?React.Ref,
+ setLocalRef: (ref: React.ElementRef) => mixed
+|}>;
+
+/**
+ * This is a helper function for when a component needs to be able to forward a ref
+ * to a child component, but still needs to have access to that component as part of
+ * its implementation.
+ *
+ * Its main use case is in wrappers for native components.
+ *
+ * Usage:
+ *
+ * function MyView(props) {
+ * const ref = useRef(null);
+ *
+ * function setRef = setAndForwardRef({
+ * getForwardedRef: () => props.forwardedRef,
+ * setLocalRef: localRef => {
+ * ref.current = localRef;
+ * },
+ * });
+ *
+ * return ;
+ * }
+ *
+ * const MyViewWithRef = React.forwardRef((props, ref) => (
+ *
+ * ));
+ */
+
+export default function setAndForwardRef({ getForwardedRef, setLocalRef }: Args) {
+ return function forwardRef(ref: React.ElementRef) {
+ const forwardedRef = getForwardedRef();
+ setLocalRef(ref);
+
+ // Forward to user ref prop (if one has been specified)
+ if (typeof forwardedRef === 'function') {
+ // Handle function-based refs. String-based refs are handled as functions.
+ forwardedRef(ref);
+ } else if (typeof forwardedRef === 'object' && forwardedRef != null) {
+ // Handle createRef-based refs
+ forwardedRef.current = ref;
+ }
+ };
+}