diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index b60ae10fa8abcf..b28cc57048395b 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -9,6 +9,7 @@ */ import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; +import type {____TextStyle_Internal as TextStyleInternal} from '../../StyleSheet/StyleSheetTypes'; import type { PressEvent, ScrollEvent, @@ -1534,10 +1535,18 @@ function InternalTextInput(props: Props): React.Node { }; } - let style = flattenStyle(props.style); - if (typeof style?.fontWeight === 'number') { - // $FlowFixMe - style = [style, {fontWeight: style.fontWeight.toString()}]; + // Keep the original (potentially nested) style when possible, as React can diff these more efficiently + let _style = props.style; + const flattenedStyle = flattenStyle(props.style); + if (typeof flattenedStyle?.fontWeight === 'number') { + _style = [ + _style, + { + fontWeight: + // $FlowFixMe[incompatible-cast] + (flattenedStyle.fontWeight.toString(): TextStyleInternal['fontWeight']), + }, + ]; } if (Platform.OS === 'ios') { @@ -1548,10 +1557,10 @@ function InternalTextInput(props: Props): React.Node { const useMultilineDefaultStyle = props.multiline === true && - (style == null || - (style.padding == null && - style.paddingVertical == null && - style.paddingTop == null)); + (flattenedStyle == null || + (flattenedStyle.padding == null && + flattenedStyle.paddingVertical == null && + flattenedStyle.paddingTop == null)); textInput = ( @@ -1645,7 +1654,7 @@ function InternalTextInput(props: Props): React.Node { onScroll={_onScroll} onSelectionChange={_onSelectionChange} placeholder={placeholder} - style={style} + style={_style} text={text} textBreakStrategy={props.textBreakStrategy} /> diff --git a/packages/react-native/Libraries/Image/Image.android.js b/packages/react-native/Libraries/Image/Image.android.js index 111abb665acd94..4fa0bdc087a1ce 100644 --- a/packages/react-native/Libraries/Image/Image.android.js +++ b/packages/react-native/Libraries/Image/Image.android.js @@ -150,24 +150,20 @@ let BaseImage: AbstractImageAndroid = React.forwardRef( ); } - let style; + let style: ImageStyleProp; let sources; if (Array.isArray(source)) { - style = flattenStyle([styles.base, props.style]); + style = [styles.base, props.style]; sources = source; } else { const {uri} = source; - const width = source.width ?? props.width; - const height = source.height ?? props.height; - style = flattenStyle([ - {width, height}, - styles.base, - props.style, - ]); - sources = [source]; if (uri === '') { console.warn('source.uri should not be an empty string'); } + const width = source.width ?? props.width; + const height = source.height ?? props.height; + style = [{width, height}, styles.base, props.style]; + sources = [source]; } const {height, width, ...restProps} = props; @@ -203,11 +199,10 @@ let BaseImage: AbstractImageAndroid = React.forwardRef( }, }; - const objectFit = style?.objectFit - ? convertObjectFitToResizeMode(style.objectFit) - : null; + const flattenedStyle = flattenStyle(style); + const objectFit = convertObjectFitToResizeMode(flattenedStyle?.objectFit); const resizeMode = - objectFit || props.resizeMode || style?.resizeMode || 'cover'; + objectFit || props.resizeMode || flattenedStyle?.resizeMode || 'cover'; const actualRef = useWrapRefWithImageAttachedCallbacks(forwardedRef); diff --git a/packages/react-native/Libraries/Image/Image.ios.js b/packages/react-native/Libraries/Image/Image.ios.js index 4b7353e2547f91..a0054db5fff72e 100644 --- a/packages/react-native/Libraries/Image/Image.ios.js +++ b/packages/react-native/Libraries/Image/Image.ios.js @@ -8,7 +8,7 @@ * @format */ -import type {ImageStyle, ImageStyleProp} from '../StyleSheet/StyleSheet'; +import type {ImageStyleProp} from '../StyleSheet/StyleSheet'; import type {RootTag} from '../Types/RootTagTypes'; import type {AbstractImageIOS, ImageIOS} from './ImageTypes.flow'; import type {ImageSize} from './NativeImageLoaderAndroid'; @@ -112,38 +112,27 @@ let BaseImage: AbstractImageIOS = React.forwardRef((props, forwardedRef) => { height: undefined, }; + let style: ImageStyleProp; let sources; - let style: ImageStyle; - if (Array.isArray(source)) { - style = - flattenStyle([styles.base, props.style]) || - ({}: ImageStyle); + style = [styles.base, props.style]; sources = source; } else { const {uri} = source; - const width = source.width ?? props.width; - const height = source.height ?? props.height; - style = - flattenStyle([ - {width, height}, - styles.base, - props.style, - ]) || ({}: ImageStyle); - sources = [source]; - if (uri === '') { console.warn('source.uri should not be an empty string'); } + const width = source.width ?? props.width; + const height = source.height ?? props.height; + style = [{width, height}, styles.base, props.style]; + sources = [source]; } - const objectFit = - style.objectFit != null - ? convertObjectFitToResizeMode(style.objectFit) - : null; + const flattenedStyle = flattenStyle(style); + const objectFit = convertObjectFitToResizeMode(flattenedStyle?.objectFit); const resizeMode = - objectFit || props.resizeMode || style.resizeMode || 'cover'; - const tintColor = props.tintColor ?? style.tintColor; + objectFit || props.resizeMode || flattenedStyle?.resizeMode || 'cover'; + const tintColor = props.tintColor ?? flattenedStyle?.tintColor; if (props.children != null) { throw new Error( diff --git a/packages/react-native/Libraries/Image/ImageUtils.js b/packages/react-native/Libraries/Image/ImageUtils.js index 78671873147d20..732b5733bc6e81 100644 --- a/packages/react-native/Libraries/Image/ImageUtils.js +++ b/packages/react-native/Libraries/Image/ImageUtils.js @@ -10,13 +10,13 @@ type ResizeMode = 'cover' | 'contain' | 'stretch' | 'repeat' | 'center'; -export function convertObjectFitToResizeMode(objectFit: string): ResizeMode { - const objectFitMap = { - contain: 'contain', - cover: 'cover', - fill: 'stretch', - 'scale-down': 'contain', - }; - // $FlowFixMe[invalid-computed-prop] - return objectFitMap[objectFit]; +const objectFitMap: {[string]: ResizeMode} = { + contain: 'contain', + cover: 'cover', + fill: 'stretch', + 'scale-down': 'contain', +}; + +export function convertObjectFitToResizeMode(objectFit: ?string): ?ResizeMode { + return objectFit != null ? objectFitMap[objectFit] : undefined; } diff --git a/packages/react-native/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap b/packages/react-native/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap index 7be68b00ebcef9..310832436732e3 100644 --- a/packages/react-native/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap +++ b/packages/react-native/Libraries/Image/__tests__/__snapshots__/Image-test.js.snap @@ -31,11 +31,16 @@ exports[`Image should render as when not mocked 1`] = ` ] } style={ - Object { - "height": undefined, - "overflow": "hidden", - "width": undefined, - } + Array [ + Object { + "height": undefined, + "width": undefined, + }, + Object { + "overflow": "hidden", + }, + undefined, + ] } /> `; diff --git a/packages/react-native/Libraries/Text/Text.js b/packages/react-native/Libraries/Text/Text.js index 72e23054fa3afc..01fe0d57a20e28 100644 --- a/packages/react-native/Libraries/Text/Text.js +++ b/packages/react-native/Libraries/Text/Text.js @@ -8,6 +8,7 @@ * @format */ +import type {TextStyleProp} from '../StyleSheet/StyleSheet'; import type {____TextStyle_Internal as TextStyleInternal} from '../StyleSheet/StyleSheetTypes'; import type {PressEvent} from '../Types/CoreEventTypes'; import type {NativeTextProps} from './TextNativeComponent'; @@ -133,7 +134,7 @@ const Text: React.AbstractComponent = let _selectable = selectable; - let processedStyle: ?TextStyleInternal = flattenStyle(_style); + let processedStyle = flattenStyle(_style); if (processedStyle != null) { let overrides: ?{...TextStyleInternal} = null; if (typeof processedStyle.fontWeight === 'number') { @@ -158,7 +159,7 @@ const Text: React.AbstractComponent = if (overrides != null) { // $FlowFixMe[incompatible-type] - processedStyle = [processedStyle, overrides]; + _style = [_style, overrides]; } } @@ -178,7 +179,7 @@ const Text: React.AbstractComponent = numberOfLines: _numberOfLines, selectable: _selectable, selectionColor: _selectionColor, - style: processedStyle, + style: _style, disabled: disabled, children, }} @@ -212,7 +213,7 @@ const Text: React.AbstractComponent = ref={forwardedRef} selectable={_selectable} selectionColor={_selectionColor} - style={processedStyle} + style={_style} disabled={disabled}> {children} @@ -256,7 +257,7 @@ const Text: React.AbstractComponent = numberOfLines: _numberOfLines, selectable: _selectable, selectionColor: _selectionColor, - style: processedStyle, + style: _style, children, }} textPressabilityProps={{ @@ -291,7 +292,7 @@ const Text: React.AbstractComponent = ref={forwardedRef} selectable={_selectable} selectionColor={_selectionColor} - style={processedStyle}> + style={_style}> {children} ); diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 32fe6394c1f5d6..528f1c7eaadd86 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -5002,8 +5002,8 @@ export type { ImageProps } from \\"./ImageProps\\"; exports[`public API should not change unintentionally Libraries/Image/ImageUtils.js 1`] = ` "type ResizeMode = \\"cover\\" | \\"contain\\" | \\"stretch\\" | \\"repeat\\" | \\"center\\"; declare export function convertObjectFitToResizeMode( - objectFit: string -): ResizeMode; + objectFit: ?string +): ?ResizeMode; " `;