1import React from 'react';
2import { Platform, StyleSheet, Text } from 'react-native';
3
4function useChildren(inputChildren: React.ReactNode) {
5  return React.useMemo(() => {
6    const children: React.ReactNode[] = [];
7    React.Children.forEach(inputChildren, (child) => {
8      if (child == null || typeof child === 'boolean') {
9      } else if (typeof child === 'string' || typeof child === 'number') {
10        // Wrap text in a Text component.
11        let message = `Invalid raw text as a child of View: "${child}"${
12          child === '' ? ` [empty string]` : ''
13        }.`;
14        message += ' Wrap the text contents with a Text element or remove it.';
15        console.warn(message);
16        children.push(
17          <Text style={[StyleSheet.absoluteFill, styles.error]}>
18            Unwrapped text: "<Text style={{ fontWeight: 'bold' }}>{child}</Text>"
19          </Text>
20        );
21      } else if ('type' in child && typeof child?.type === 'string' && Platform.OS !== 'web') {
22        // Disallow untransformed react-dom elements on native.
23        throw new Error(`Using unsupported React DOM element: <${child.type} />`);
24      } else {
25        children.push(child);
26      }
27    });
28    return children;
29  }, [inputChildren]);
30}
31
32/** Extend a view with a `children` filter that asserts more helpful warnings/errors. */
33export function createDevView<TView extends React.ComponentType<any>>(View: TView) {
34  return React.forwardRef(({ children, ...props }: any, forwardedRef: React.Ref<TView>) => {
35    return <View ref={forwardedRef} {...props} children={useChildren(children)} />;
36  });
37}
38
39const styles = StyleSheet.create({
40  error: {
41    backgroundColor: 'firebrick',
42    color: 'white',
43  },
44});
45