Skip to content

Commit da1cc48

Browse files
committed
Adopt 'experimental_LayoutConformance' from React Native
React Native removed the 'experimental_layoutConformance' prop from View and introduced the 'experimental_LayoutConformance' component. This new component is expected to wrap any tree that needs to opt into W3C layout. Use of the new component now requires explicit setting of a prop on 'html.*' elements - 'data-layoutconformance="strict"'. This is done to avoid wrapping every `View` in a `LayoutConformance` component, since that now alters the component tree rather than just adding a prop. Adding a prop to RSD elements is also preferred because it avoids two other problems: 1) Developers having to import from 'react-native' in a file that might otherwise be expected to be cross-platform, 2) React Native for Web having to add support for this export to avoid crashing Expo apps (unless using file forks.) The use of another special 'data-*' attribute avoids React DOM warnings about unrecognized properties on web. Fix #245
1 parent 2e68c68 commit da1cc48

12 files changed

Lines changed: 51 additions & 84 deletions

File tree

apps/examples/src/components/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ function Shell(): React.MixedElement {
145145

146146
return (
147147
<ScrollView>
148-
<html.div style={egStyles.div}>
148+
<html.div data-layoutconformance="strict" style={egStyles.div}>
149149
<ExampleBlock title="HTML elements">
150150
<html.div>Text inside div (kind of) works</html.div>
151151
<html.span>

apps/website/docs/learn/02-environment-setup.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,23 @@ import './stylex.css';
128128
import '@expo/metro-runtime';
129129

130130
import { registerRootComponent } from 'expo';
131-
import App from './App';
131+
import { App } from './App';
132132

133133
registerRootComponent(App);
134134
```
135135
136+
To enable React Native support for layout that better conforms to W3C standards, you must set `data-layoutconformance="strict"` on the root `html.*` element of your app.
137+
138+
```js title="App.js"
139+
export function App() {
140+
return (
141+
<html.div data-layoutconformance="strict">
142+
{/* The rest of your app component */}
143+
</html.div>
144+
);
145+
}
146+
```
147+
136148
## Platform-specific files
137149
138150
Expo supports [platform-specific extensions](https://docs.expo.dev/router/advanced/platform-specific-modules/#platform-specific-extensions) by default. This allows you to create platform-specific implementations of components, hooks, etc.

packages/benchmarks/perf/mocks/react-native.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export const Text = 'Text';
106106

107107
export const View = 'View';
108108

109+
export const experimental_LayoutConformance = 'LayoutConformance';
110+
109111
export const useColorScheme = () => 'light';
110112

111113
export const useWindowDimensions = () => ({

packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { StrictProps as StrictPropsOriginal } from '../../types/StrictProps
1212

1313
import * as React from 'react';
1414
import { Animated, Pressable } from 'react-native';
15+
import { experimental_LayoutConformance as LayoutConformance } from 'react-native';
1516
import { ViewNativeComponent, TextAncestorContext } from '../react-native';
1617

1718
import { ProvideCustomProperties } from './ContextCustomProperties';
@@ -150,11 +151,6 @@ export function createStrictDOMComponent<T, P: StrictProps>(
150151
* Construct tree
151152
*/
152153

153-
if (NativeComponent === ViewNativeComponent) {
154-
// enable W3C flexbox layout
155-
nativeProps.experimental_layoutConformance = 'strict';
156-
}
157-
158154
let element: React.Node =
159155
typeof props.children === 'function' ? (
160156
props.children(nativeProps)
@@ -163,6 +159,11 @@ export function createStrictDOMComponent<T, P: StrictProps>(
163159
<NativeComponent {...nativeProps} />
164160
);
165161

162+
// Enable W3C layout support
163+
if (props['data-layoutconformance'] === 'strict') {
164+
element = <LayoutConformance children={element} mode="strict" />;
165+
}
166+
166167
if (
167168
(nativeProps.children != null &&
168169
typeof nativeProps.children !== 'string') ||

packages/react-strict-dom/src/shared/isPropAllowed.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,14 @@ const strictAttributeSet: Set<string> = new Set([
135135
'srcSet', // img
136136
'step', // input
137137
'style',
138-
'suppressHydrationWarning',
139138
'tabIndex',
140139
'target', // a
141140
'type', // button, input
142141
'value', // input
143142
'width', // img
144143

144+
'suppressHydrationWarning', // React
145+
145146
'onMouseDown', // TEMPORARY
146147
'onMouseEnter', // TEMPORARY
147148
'onMouseLeave', // TEMPORARY

packages/react-strict-dom/src/types/StrictReactDOMProps.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ export type StrictReactDOMProps = $ReadOnly<{
290290
),
291291
autoFocus?: ?boolean,
292292
children?: React.Node,
293+
'data-layoutconformance'?: ?('classic' | 'strict'),
293294
'data-testid'?: ?string,
294295
dir?: ?('auto' | 'ltr' | 'rtl'),
295296
elementTiming?: ?string,

packages/react-strict-dom/src/types/renderer.native.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ type ReactNativeProps = {
6161
disabled?: ?boolean,
6262
editable?: TextInputProps['editable'],
6363
enterKeyHint?: TextInputProps['enterKeyHint'],
64-
experimental_layoutConformance?: 'strict',
6564
focusable?: ?boolean,
6665
height?: ImageProps['height'],
6766
importantForAccessibility?: 'no-hide-descendants',

packages/react-strict-dom/tests/__mocks__/react-native/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ export const Text = 'Text';
102102

103103
export const View = 'View';
104104

105+
export const experimental_LayoutConformance = 'LayoutConformance';
106+
105107
export const useColorScheme = jest.fn().mockReturnValue('light');
106108

107109
export const useWindowDimensions = jest

packages/react-strict-dom/tests/__snapshots__/compat-test.native.js.snap-native

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ exports[`<compat.native> "as" equals "textarea": as=textarea 1`] = `
6464
exports[`<compat.native> "as" equals "view": as=view 1`] = `
6565
<View
6666
accessibilityLabel="label"
67-
experimental_layoutConformance="strict"
6867
ref={[Function]}
6968
style={
7069
{
@@ -79,7 +78,6 @@ exports[`<compat.native> "as" equals "view": as=view 1`] = `
7978
exports[`<compat.native> default: default 1`] = `
8079
<Pressable
8180
accessibilityLabel="label"
82-
experimental_layoutConformance="strict"
8381
ref={[Function]}
8482
style={
8583
{
@@ -94,7 +92,6 @@ exports[`<compat.native> default: default 1`] = `
9492
exports[`<compat.native> nested: nested 1`] = `
9593
<View
9694
accessibilityLabel="label"
97-
experimental_layoutConformance="strict"
9895
ref={[Function]}
9996
style={
10097
{

0 commit comments

Comments
 (0)