From 85e74ebdc835cec83bdd0b4345da948552e4710d Mon Sep 17 00:00:00 2001 From: qflen Date: Wed, 6 May 2026 15:22:25 +0200 Subject: [PATCH 1/2] Log dev error for React.Fragment child in TouchableHighlight Fixes #54933. TouchableHighlight injects the underlay style onto its single child via cloneElement. React.Fragment cannot accept that style, so React emits a generic "Invalid prop `style` supplied to `React.Fragment`" warning and the highlight effect is silently broken. Surface a clear console.error in __DEV__ naming the component, the constraint, and the fix (wrap in a View). Skip the cloneElement when the child is a Fragment so React's generic warning no longer fires on top. Render the Fragment unchanged so apps relying on this since 0.79 do not crash on upgrade. Pattern matches dev error logging used elsewhere in RN such as ScrollView and TextInputState. --- .../Touchable/TouchableHighlight.js | 20 ++++++--- .../__tests__/TouchableHighlight-itest.js | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js b/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js index 0064e996238a..3c90587135e6 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js @@ -304,6 +304,12 @@ class TouchableHighlightImpl extends React.Component< render(): React.Node { const child = React.Children.only<$FlowFixMe>(this.props.children); + if (__DEV__ && child.type === React.Fragment) { + console.error( + 'TouchableHighlight does not support React.Fragment as a child. ' + + 'Wrap the children in a single host element such as .', + ); + } // BACKWARD-COMPATIBILITY: Focus and blur events were never supported before // adopting `Pressability`, so preserve that behavior. @@ -376,12 +382,14 @@ class TouchableHighlightImpl extends React.Component< testID={this.props.testID} ref={this.props.hostRef} {...eventHandlersWithoutBlurAndFocus}> - {cloneElement(child, { - style: StyleSheet.compose( - child.props.style, - this.state.extraStyles?.child, - ), - })} + {child.type === React.Fragment + ? child + : cloneElement(child, { + style: StyleSheet.compose( + child.props.style, + this.state.extraStyles?.child, + ), + })} {__DEV__ ? ( ) : null} diff --git a/packages/react-native/Libraries/Components/Touchable/__tests__/TouchableHighlight-itest.js b/packages/react-native/Libraries/Components/Touchable/__tests__/TouchableHighlight-itest.js index 715603bb6583..2ba6b8c370f9 100644 --- a/packages/react-native/Libraries/Components/Touchable/__tests__/TouchableHighlight-itest.js +++ b/packages/react-native/Libraries/Components/Touchable/__tests__/TouchableHighlight-itest.js @@ -395,6 +395,48 @@ describe('', () => { , ); }); + + // Regression test for #54933: a `React.Fragment` cannot accept the + // underlay style applied via `cloneElement`, so the highlight effect + // is silently broken. Render the Fragment unchanged and surface an + // actionable dev error pointing the user at wrapping in . + it('logs a dev error when given a React.Fragment as a child', () => { + const originalConsoleError = console.error; + const mockConsoleError = jest.fn(); + // $FlowFixMe[cannot-write] + console.error = mockConsoleError; + + try { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + + + First + Second + + , + ); + }); + + expect( + root.getRenderedOutput({props: ['accessible']}).toJSX(), + ).toEqual( + + First + Second + , + ); + expect(mockConsoleError).toHaveBeenCalledTimes(1); + expect(mockConsoleError.mock.calls[0][0]).toContain( + 'TouchableHighlight does not support React.Fragment as a child', + ); + } finally { + // $FlowFixMe[cannot-write] + console.error = originalConsoleError; + } + }); }); }); From 201945ea1360e762e45cf28ebe141aa00eb948fd Mon Sep 17 00:00:00 2001 From: qflen Date: Wed, 6 May 2026 16:06:51 +0200 Subject: [PATCH 2/2] Trigger CI re-check after artifact upload flake