preactjs / signals

Manage state with style in every framework
https://preactjs.com/blog/introducing-signals/
MIT License
3.72k stars 91 forks source link

Error when using signals with react-native navigation #257

Open MohamedJerbi opened 1 year ago

MohamedJerbi commented 1 year ago

The error is easy to replicate. packadge.json

{
  "name": "",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
  },
  "dependencies": {
    "@preact/signals-react": "^1.2.1",
    "@react-navigation/bottom-tabs": "^6.4.0",
    "@react-navigation/native": "^6.0.13",
    "@react-navigation/native-stack": "^6.9.1",
    "i18next": "^22.0.4",
    "react": "18.1.0",
    "react-i18next": "^12.0.0",
    "react-native": "0.70.3",
    "react-native-gesture-handler": "^2.8.0",
    "react-native-material-dropdown": "^0.11.1",
    "react-native-paper": "^5.0.0-rc.8",
    "react-native-safe-area-context": "^4.4.1",
    "react-native-screens": "^3.16.0",
    "react-native-svg": "^13.4.0",
    "react-native-vector-icons": "^9.2.0"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/runtime": "^7.12.5",
    "@react-native-community/eslint-config": "^2.0.0",
    "@tsconfig/react-native": "^2.0.2",
    "@types/jest": "^26.0.23",
    "@types/react": "^18.0.21",
    "@types/react-native": "^0.70.4",
    "@types/react-native-material-dropdown": "^0.11.5",
    "@types/react-test-renderer": "^18.0.0",
    "@typescript-eslint/eslint-plugin": "^5.37.0",
    "@typescript-eslint/parser": "^5.37.0",
    "babel-jest": "^26.6.3",
    "eslint": "^7.32.0",
    "jest": "^26.6.3",
    "metro-react-native-babel-preset": "0.72.3",
    "react-test-renderer": "18.1.0",
    "typescript": "^4.8.3"
  },
  "jest": {
    "preset": "react-native",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ]
  }
}

How to reproduce.

  1. Setup react-native navigation example
    
    const App = () => (
    <NavigationContainer>
    <Stack.Navigator
      initialRouteName={initialRouteName}
      screenOptions={noHeaderOption}>
      <Stack.Screen name="Login" component={Login} />
    </Stack.Navigator>
    </NavigationContainer>
    );

2. use react signals here
I have another file containing an exported signal

import {Signal, signal} from '@preact/signals-react'; import {User} from '../types';

const user: Signal = signal(null); // get from session storage?

export const initialRouteName = user ? 'BottomTab' : 'Login';

export default user;

Once I import this signal inside the Login component and use it there, I get the error
Error: A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'Screen' for the screen 'Login'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.

This error is located at: in NativeStackNavigator (created by App) in EnsureSingleNavigator in BaseNavigationContainer in ThemeProvider in NavigationContainerInner (created by App) in App (created by Main) in ThemeProvider (created by Provider) in RCTView (created by View) in View (created by Portal.Host) in Portal.Host (created by Provider) in Provider (created by Main) in Main in RCTView (created by View) in View (created by AppContainer) in RCTView (created by View) in View (created by AppContainer) in AppContainer in thedoc(RootComponent), js engine: hermes

elliotwaite commented 1 year ago

I am running into this same error.

Seanmclem commented 1 year ago

same here..

mustafaskyer commented 1 year ago

This issues happens in react-navigation as well I've disabled these lines and it works as expected. at this file File

// React.createElement = WrapJsx(React.createElement);
// JsxDev.jsx && /*   */ (JsxDev.jsx = WrapJsx(JsxDev.jsx));
// JsxPro.jsx && /*   */ (JsxPro.jsx = WrapJsx(JsxPro.jsx));
// JsxDev.jsxs && /*  */ (JsxDev.jsxs = WrapJsx(JsxDev.jsxs));
// JsxPro.jsxs && /*  */ (JsxPro.jsxs = WrapJsx(JsxPro.jsxs));
// JsxDev.jsxDEV && /**/ (JsxDev.jsxDEV = WrapJsx(JsxDev.jsxDEV));
// JsxPro.jsxDEV && /**/ (JsxPro.jsxDEV = WrapJsx(JsxPro.jsxDEV));

So, I suggest to disable these lines at ios and android platforms. @JoviDeCroock

raphaelott commented 1 year ago

Facing the same issue with a BottomTabNavigator in React Native. Disabling the lines as mentioned above didnt work for me either.

suman379 commented 1 year ago

Facing the same problem.

Voznov commented 1 year ago

I fixed this issue (it works for 1.2.1 version). You need to apply this patch for @react-navigation/core:

diff --git a/node_modules/@react-navigation/core/src/Group.tsx b/node_modules/@react-navigation/core/src/Group.tsx
index 352ba31..893d2c2 100644
--- a/node_modules/@react-navigation/core/src/Group.tsx
+++ b/node_modules/@react-navigation/core/src/Group.tsx
@@ -12,3 +12,4 @@ export default function Group<
   /* istanbul ignore next */
   return null;
 }
+Group.__isGroup = true;
\ No newline at end of file
diff --git a/node_modules/@react-navigation/core/src/Screen.tsx b/node_modules/@react-navigation/core/src/Screen.tsx
index 4ca4306..f116e61 100644
--- a/node_modules/@react-navigation/core/src/Screen.tsx
+++ b/node_modules/@react-navigation/core/src/Screen.tsx
@@ -15,3 +15,4 @@ export default function Screen<
   /* istanbul ignore next */
   return null;
 }
+Screen.__isScreen = true;
\ No newline at end of file
diff --git a/node_modules/@react-navigation/core/src/useNavigationBuilder.tsx b/node_modules/@react-navigation/core/src/useNavigationBuilder.tsx
index 76442df..e1c8632 100644
--- a/node_modules/@react-navigation/core/src/useNavigationBuilder.tsx
+++ b/node_modules/@react-navigation/core/src/useNavigationBuilder.tsx
@@ -93,7 +93,7 @@ const getRouteConfigsFromChildren = <
     ScreenConfigWithParent<State, ScreenOptions, EventMap>[]
   >((acc, child) => {
     if (React.isValidElement(child)) {
-      if (child.type === Screen) {
+      if (child.type?.__isScreen) {
         // We can only extract the config from `Screen` elements
         // If something else was rendered, it's probably a bug

@@ -121,7 +121,7 @@ const getRouteConfigsFromChildren = <
         return acc;
       }

-      if (child.type === React.Fragment || child.type === Group) {
+      if (child.type === React.Fragment || child.type?.__isGroup) {
         if (!isValidKey(child.props.navigationKey)) {
           throw new Error(
             `Got an invalid 'navigationKey' prop (${JSON.stringify(