react-tv-space-navigation icon indicating copy to clipboard operation
react-tv-space-navigation copied to clipboard

React native 0.78 compatibility

Open cristiammercado opened this issue 10 months ago β€’ 32 comments

Describe the bug Hi,

I'm in the process of migrating to React Native 0.78.0 and React 19, and there appear to be compatibility issues with the library in version 5.1.1.

To Reproduce

import {Directions, SpatialNavigation} from 'react-tv-space-navigation';

import {LoggerService} from './logger.service.ts';
import {remoteControlService} from './remote-control.service.ts';
import {SupportedKeysEnum} from '../model/enums/supported-keys.enum.ts';

export class SpatialNavigationService {

  private constructor() {
  }

  public static init(): void {
    SpatialNavigation.configureRemoteControl({
      remoteControlSubscriber: (callback) => {
        const mapping: { [key in SupportedKeysEnum]: Directions | null } = {
          [SupportedKeysEnum.Right]: Directions.RIGHT,
          [SupportedKeysEnum.Left]: Directions.LEFT,
          [SupportedKeysEnum.Up]: Directions.UP,
          [SupportedKeysEnum.Down]: Directions.DOWN,
          [SupportedKeysEnum.Enter]: Directions.ENTER,
          [SupportedKeysEnum.Back]: null
        };

        const remoteControlListener = (keyEvent: SupportedKeysEnum): void => callback(mapping[keyEvent]);

        return remoteControlService.addKeydownListener(remoteControlListener);
      },
      remoteControlUnsubscriber: (remoteControlListener): void => {
        remoteControlService.removeKeydownListener(remoteControlListener);
      },
    });

    LoggerService.info(`[SpatialNavigationService] Initialized spatial navigation service on TV`);
  }

}

<SpatialNavigationDeviceTypeProvider>
  <SpatialNavigationRoot isActive={true}>
    <SpatialNavigationView direction='vertical' style={styles.container}>
      <MyAppComponent/>
    </SpatialNavigationView>
  </SpatialNavigationRoot>
</SpatialNavigationDeviceTypeProvider>

Expected behavior Application continues to work as it normally did in previous versions of React native (in my case 0.76.5).

Screenshots

Image

Image

Image

Version and OS

  • Library version: 5.1.1
  • React Native version: 0.78.0
  • OS: Android

Additional context Add any other context about the problem here.

cristiammercado avatar Mar 14 '25 14:03 cristiammercado

Hi @pierpo, any update about this?

cristiammercado avatar Mar 27 '25 13:03 cristiammercado

Hi @pierpo any update on this ?

samsonroy avatar Apr 14 '25 01:04 samsonroy

@pierpo - Any update on this, We are having issue with RN version 0.78, I'm in the process of migrating to React Native 0.78.0 and React 19, and there appear to be compatibility issues with the library.

vishu2124 avatar Apr 14 '25 10:04 vishu2124

Hey! Sorry for not being very active on the project right now πŸ˜… Could you help with the investigation? I do not have any time to look at it myself 😭 I'll make some time to review PRs and publish changes, but it would be lovely to get some help regarding the solution!

pierpo avatar Apr 29 '25 16:04 pierpo

@pierpo , The React and React-dom version seem to cause this issue. Since the latest version of expo and react-native are using version 19 and SpatialNavigation is using 18.

"@types/react": "~18.2.79", "@types/react-dom": "~18.2.25",

robmim avatar May 01 '25 15:05 robmim

@pierpo Just made a PR for this. Everything is building fine but the mapping of the remote control inst working now. Why i dont know.

https://github.com/bamlab/react-tv-space-navigation/pull/185

robmim avatar May 01 '25 21:05 robmim

Awesome @robmim πŸ™Œ I will have a look! Sorry about the very long delay, I was off the whole month!

pierpo avatar Jun 09 '25 15:06 pierpo

Any update on this @robmim @pierpo ?

fuecco avatar Jul 07 '25 14:07 fuecco

@fuecco I strongly recommend you to use react-native-tv-os instead of this librairy if you need to go forward with your project.

There is plenty of demo and sample project you can based your project on.

https://github.com/react-native-tvos/react-native-tvos/wiki

robmim avatar Jul 07 '25 16:07 robmim

Any reason for not recommending this library? Do you see any issues/shortcomings?

fuecco avatar Jul 07 '25 16:07 fuecco

I'm simply saying that if you need to go forward you should think about using something else. Putting pressure on Pierre or me to fix issues you have doesn't help anyone here. If you want to contribute and help us you are more than welcome to edit code, add pr, find bugs.

robmim avatar Jul 07 '25 16:07 robmim

@fuecco maybe have a look to this one too. Just found that and really thinking about trying it myself

https://github.com/NoriginMedia/Norigin-Spatial-Navigation

robmim avatar Jul 09 '25 14:07 robmim

Sorry it's taking so long. I'll try to merge the recent React support this week!

pierpo avatar Jul 10 '25 11:07 pierpo

just started setting up new project and ran into this issue at first run !!! @robmim any documentation on how to switch from this package to "react-native-tv-os"

ust1957 avatar Jul 17 '25 03:07 ust1957

I published v6.0.0-beta1 to unblock you. You might give it a try. I can't merge my PR yet because tests are broken for now πŸ€” (for dependencies issues reasons, not features being broken)

pierpo avatar Jul 17 '25 07:07 pierpo

I finally merged the PR, but I'd still like a bit of feedback on the beta before publishing this as 6.0.0 😁

https://github.com/bamlab/react-tv-space-navigation/pull/200

pierpo avatar Jul 18 '25 15:07 pierpo

@pierpo still on vacation until monday. Will have a look when i will be back

robmim avatar Jul 18 '25 21:07 robmim

It is working for me on Android, but not on Web. The error is generated by the SpatialNavigationScrollView component

Image

lKinderBueno avatar Jul 20 '25 18:07 lKinderBueno

Hey @lKinderBueno, do you have more details to reproduce or a piece of code? Web is working fine on the lib example with the upgrade merged πŸ€”

pierpo avatar Jul 22 '25 12:07 pierpo

@pierpo I used your example. The only changes are:

  • Added missing dependencies in package.json
  • Updated imports to point to react-tv-space-navigation instead of the local file path (ex. from import { SpatialNavigationVirtualizedListRef } from '../../../../../lib/src/spatial-navigation/types/SpatialNavigationVirtualizedListRef'; to import { SpatialNavigationVirtualizedListRef } from 'react-tv-space-navigation';
  • Remove most of the images from modules folder (github limits the upload to max 20MB. The zip was larger than 30)

The zip contains two package.json variants:

  • package-5.2.0.json
  • package-6.0.0.json

To run the example with Reactβ€―native 18 and react-tv-space-navigation v5.2.0:

  1. Rename package-5.2.0.json to package.json.
  2. Run npm install --legacy-peer-deps
  3. Start with npx expo start --clear
  4. Then press "w" to open the web version

For the 6.0.0 version, just rename package-6.0.0.json to package.json and repeat the same commands. For the 6.0.0 version, the browser should display a blank image with the error reported in previous message. The 5.2.0 version works correctly.

You can grab everything here: test.zip

lKinderBueno avatar Jul 22 '25 16:07 lKinderBueno

Salut @pierpo πŸ‘‹

I can confirm that similar to @lKinderBueno - Although v6.0.0-beta1 seems to be a great step towards React 19 support by fixing error Cannot read properties of undefined (reading 'ReactCurrentOwner').

There still seems to be some incompatibility when using components <SpatialNavigationScrollView /> and <SpatialNavigationVirtualizedList /> that cause the following error:

Cannot read properties of undefined (reading 'validated')

Thank you for the great package!

iAMkVIN-S avatar Jul 31 '25 00:07 iAMkVIN-S

Thanks both of you. I need to investigate this then! Any help is appreciated, I know this is a blocker for the library 😭

pierpo avatar Jul 31 '25 12:07 pierpo

Bonsoir @pierpo πŸ‘‹

After quite a bit of digging, I noticed a few things:

(1) Since the error happens when using <SpatialNavigationScrollView /> and <SpatialNavigationVirtualizedList /> I tried to find a pattern and noticed that both were using the same context.

(2) The context (SpatialNavigatorParentScrollContext) at packages/lib/spatial-navigation/context/ParentScrollContext.ts was initializing with an empty default value of () => {}

(3) I changed the default value to undefined and setup an error if the context was attempted to be used outside the provider:

export const SpatialNavigatorParentScrollContext = createContext<ScrollToNodeCallback | undefined>(undefined);

export const useSpatialNavigatorParentScroll = (): { scrollToNodeIfNeeded: ScrollToNodeCallback } => {
  const scrollToNodeIfNeeded = useContext(SpatialNavigatorParentScrollContext);
  if (!scrollToNodeIfNeeded) {
    throw new Error('useSpatialNavigatorParentScroll must be used within SpatialNavigatorParentScrollContext.Provider');
  }
  return { scrollToNodeIfNeeded };
};
Image

(4) I rebuilt the package with my changes above, and replaced the /dist in my nodes_modules with my new changes. I immediately got the error that the context was being used outside the provider. (Which React 19 is more strict than before)

(4.1) I also ran these changes directly in the packages/example:

Image

(5) I can fix the error if I remove references to useSpatialNavigatorParentScroll() in the following two components: A - Node.tsx (packages/lib/src/spatial-navigation/components/Node.tsx) B - ScrollView.tsx (packages/lib/src/spatial-navigation/components/ScrollView.tsx)

TLDR: This seems to confirm that the root cause is that useSpatialNavigatorParentScroll() is being used outside of the SpatialNavigatorParentScrollContext.Provider (which I believe React 19 is more strict about than prior versions)

I suspect these are the ones I am running into - but it would be prudent to apply safer checking throughout all components

I know @lKinderBueno provided you with his reproducible code - For reference, I am using AmazonAppDev's tv template which I've been updating to use expo 53, react 19 and react-native 0.79. This is my last package to update, once fixed, I will PR their react-native-multi-tv-app-sample with the upgrades!

iAMkVIN-S avatar Aug 01 '25 00:08 iAMkVIN-S

Hey @iAMkVIN-S ! Thank you so much for the deep dive. That's super helpful. I will have a look as well! It's a bit weird to have an issue because of the context consumer is used outside the provider. It's supposed to use the default value in this case (which is a convenient and expected feature of the context) πŸ€” Also, I haven't heard of this breaking change.

I will have a look.

Thank you all for your help, I'm sure we'll be able to release this soon!

pierpo avatar Aug 06 '25 08:08 pierpo

So I just had a look! I really can't manage to reproduce.

I tried:

  • to set up your repository @lKinderBueno, but I can't build the app. watch man just times out no matter, I don't understand why. So I can't try it πŸ€”
  • I tried to extract the example app, replaced the imports as you did, and tried to install [email protected]... and it worked well πŸ€”
  • I also tried to have Node components outside a ScrollView context... It worked well without any crash πŸ€”

About the context hypothesis: I really think that having a consumer outside a provider is not a problem. It would be a huge breaking change and it really is a feature (it's the only point of the context's default value, actually).

@iAMkVIN-S if you have another reproducible example to share I would be super grateful. I can't investigate this since I can't reproduce at all 😭

Edit: I just saw that you did give an example. I will have another look tomorrow! Thank you!

pierpo avatar Aug 06 '25 12:08 pierpo

Bon matin @pierpo πŸ‘‹,

Thank you so much for investigating πŸ™ I must be wrong on the underlaying cause of the error... but can assure you the error itself does exist!

I'm currently on mobile, but let me try to get you some steps to get the error on your side:

(1) Clone the AmazonAppDev React Native Multi-Tv App Sample

git clone https://github.com/amazonappdev/react-native-multi-tv-app-sample.git

(2) Replace the package.json with these up-to-date package versions

{
  "name": "test",
  "main": "expo-router/entry",
  "version": "1.0.0",
  "scripts": {
    "start": "EXPO_TV=1 expo start",
    "android": "EXPO_TV=1 expo run:android",
    "ios": "EXPO_TV=1 expo run:ios",
    "web": "expo start --web",
    "test": "jest --watchAll",
    "lint": "expo lint",
    "prebuild": "EXPO_TV=1 expo prebuild --clean",
    "prepare": "husky",
    "format": "prettier --write ."
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@bam.tech/react-native-keyevent-expo-config-plugin": "^1.0.50",
    "@expo/vector-icons": "^14.1.0",
    "@react-navigation/drawer": "^7.1.1",
    "@react-navigation/native": "^7.0.14",
    "@typescript-eslint/eslint-plugin": "^8.12.2",
    "@typescript-eslint/parser": "^8.12.2",
    "expo": "^53.0.20",
    "expo-build-properties": "~0.14.8",
    "expo-constants": "~17.1.7",
    "expo-dev-client": "~5.2.4",
    "expo-font": "~13.3.2",
    "expo-linear-gradient": "~14.1.5",
    "expo-linking": "~7.1.7",
    "expo-router": "~5.1.4",
    "expo-splash-screen": "~0.30.10",
    "expo-status-bar": "~2.2.3",
    "expo-system-ui": "~5.0.10",
    "expo-web-browser": "~14.2.0",
    "lint-staged": "^16.1.2",
    "react": "19.0.0",
    "react-dom": "19.0.0",
    "react-native": "npm:react-native-tvos@~0.79.5-0",
    "react-native-gesture-handler": "~2.24.0",
    "react-native-keyevent": "^0.3.2",
    "react-native-reanimated": "~3.17.4",
    "react-native-safe-area-context": "5.4.0",
    "react-native-screens": "~4.11.1",
    "react-native-video": "^6.8.0",
    "react-native-web": "~0.21.0",
    "react-tv-space-navigation": "^v6.0.0-beta1"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@commitlint/cli": "^19.5.0",
    "@commitlint/config-conventional": "^19.5.0",
    "@react-native-tvos/config-tv": "^0.1.3",
    "@types/jest": "^29.5.12",
    "@types/react": "^19.1.9",
    "@types/react-test-renderer": "^19.1.0",
    "babel-plugin-module-resolver": "^5.0.2",
    "eslint": "^9.13.0",
    "eslint-plugin-prettier": "^5.2.1",
    "eslint-plugin-react": "^7.37.1",
    "eslint-plugin-react-native": "^5.0.0",
    "husky": "^9.1.6",
    "jest": "^30.0.5",
    "jest-expo": "~53.0.9",
    "prettier": "^3.6.2",
    "react-native-pixel-perfect": "^1.0.2",
    "react-test-renderer": "19.1.1",
    "typescript": "~5.3.3"
  },
  "lint-staged": {
    "*.js": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.tsx": [
      "prettier --write"
    ]
  },
  "expo": {
    "install": {
      "exclude": [
        "react-native"
      ]
    }
  },
  "private": true
}

(3) Install the dependencies

npm install --legacy-peer-deps

(4) Run the web project

npx expo start --web

You should get the error right away. For reference, you will not get the error if you skip step 2 (use old package versions) or if you update the packages but comment out lines 167-174 of app/(drawer)/index.ts

<SpatialNavigationScrollView
          offsetFromStart={scaledPixels(60)}
          style={styles.scrollContent}
        >
          {renderScrollableRow("Trending Movies", trendingRef)}
          {renderScrollableRow("Classics", classicsRef)}
          {renderScrollableRow("Hip and Modern", hipAndModernRef)}
        </SpatialNavigationScrollView>

Hope this helps and sorry if instructions are missing, I wrote this from the car on my phone πŸ₯²

EDIT: After updating the packages, if memory serves me well you get an error that is fixed by removing all the imports of React (import React from "react" sometimes the import is joined with other things like useEffect or useState) In components/player/VideoPlayer.tsx add import { forwardRef } from "react"; and replace

const VideoPlayer = React.forwardRef<VideoRef, VideoPlayerProps>(

with

const VideoPlayer = forwardRef<VideoRef, VideoPlayerProps>(

iAMkVIN-S avatar Aug 06 '25 12:08 iAMkVIN-S

Thank you so much @iAMkVIN-S! It turns out I had issues on this repo as well, because my watchman was outdated. Sorry that I didn't spot it yesterday on the other example. I got to reproduce super quickly and easily. Thank you both.

Good news: I made some progress. It does come from the ScrollView. And more precisely, from the CustomScrollView, which is a ScrollView that we implemented with a CSS scroll for a smoother behaviour.

If you use useNativeScroll on the scroll views, I think the beta will probably work.

In the meantime, I will investigate what precisely in the CustomScrollView causes the crash πŸ€”

pierpo avatar Aug 07 '25 12:08 pierpo

Wow. I have cut down the problem to this:

On lib's end

// ScrollView.tsx
import { View } from 'react-native';

export const SpatialNavigationScrollView = () => {
  return (
    <View>
      <View />
    </View>
  );
};
SpatialNavigationScrollView.displayName = 'SpatialNavigationScrollView';

On example's end

import { useIsFocused } from '@react-navigation/native';
import { useMenuContext } from '../../components/MenuContext';
import { SpatialNavigationRoot, SpatialNavigationScrollView } from 'react-tv-space-navigation';

export default function IndexScreen() {
  const { isOpen: isMenuOpen } = useMenuContext();
  const isFocused = useIsFocused();
  const isActive = isFocused && !isMenuOpen;

  return (
    <SpatialNavigationRoot isActive={isActive}>
      <SpatialNavigationScrollView />
    </SpatialNavigationRoot>
  );
}

**This reproduces the problem πŸ˜… **

However, removing one nested view from the scroll view removes the issue. I don't understand.

// ScrollView.tsx
import { View } from 'react-native';

export const SpatialNavigationScrollView = () => {
  // βœ… This works. WTF πŸ’£πŸ€―
  return (
    <View />
  );
};
SpatialNavigationScrollView.displayName = 'SpatialNavigationScrollView';

I'll keep investigating tomorrow!

pierpo avatar Aug 07 '25 12:08 pierpo

I kept looking a bit, and I found that this has probably been solved in React 19.1.0

https://github.com/facebook/react/pull/32117

It looks like the problem happens with 3rd party libraries (like this one) when they are served bundled only for production mode πŸ€” I got mixed up a bit and I can't reproduce anymore, but if any one of you could check that React 19.1.0 does work I would appreciate πŸ€— I'll keep investigating and try to find a proper fix!

pierpo avatar Aug 08 '25 12:08 pierpo

I kept looking a bit, and I found that this has probably been solved in React 19.1.0

facebook/react#32117

It looks like the problem happens with 3rd party libraries (like this one) when they are served bundled only for production mode πŸ€” I got mixed up a bit and I can't reproduce anymore, but if any one of you could check that React 19.1.0 does work I would appreciate πŸ€— I'll keep investigating and try to find a proper fix!

I ran some checks with React 19.1:

  • Web: works fine.
  • Android: breaks due to a version mismatch. React 19.1 needs [email protected], which comes with RN 0.80.
  • RN 0.78 and Expo 53 only supports React 19.0.
  • React 19.1 is supported starting with RN 0.80, supported starting from Expo version 54. but 54 is still canary, not production-ready.

I tried Expo 54 and everything worked on both Android and web. Given the canary status, my suggestion is to stick with Expo 51 for now and wait Expo 54 to be officially released.

This is the package.json I used:

{
  "name": "hoppixtv",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "EXPO_NO_CLIENT_ENV_VARS=1 EXPO_TV=1 expo run:ios",
    "web": "expo start --web",
    "build:web": "expo export -p web",
    "test:types": "tsc",
    "prebuild": "expo prebuild --clean"
  },
  "dependencies": {
    "@bam.tech/react-native-keyevent-expo-config-plugin": "^1.0.52",
    "@emotion/native": "^11.11.0",
    "@emotion/react": "^11.11.3",
    "@react-native-tvos/config-tv": "^0.0.4",
    "@react-navigation/bottom-tabs": "^6.5.11",
    "@expo/metro-runtime": "~5.0.4",
    "@react-navigation/native": "^6.1.9",
    "@react-navigation/native-stack": "^6.9.17",
    "@types/jest": "^29.5.12",
    "@types/react-test-renderer": "^19.1.0",
    "babel-jest": "^29.7.0",
    "expo": "54.0.0-canary-20250729-d8899ae",
    "lodash": "^4.17.10",
    "expo-status-bar": "~2.2.3",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "jest-watch-typeahead": "^2.2.2",
    "lucide-react-native": "^0.335.0",
    "react": "19.1.0",
    "react-native": "npm:[email protected]",
    "react-dom": "19.1.0",
    "react-native-keyevent": "^0.3.2",
    "react-native-safe-area-context": "5.4.0",
    "react-native-screens": "~4.11.1",
    "react-native-svg": "15.12.1",
    "react-native-web": "~0.21.0",
    "react-tv-space-navigation": "^6.0.0-beta1",
    "typescript": "~5.8.3"
  },
  "devDependencies": {
    "@babel/core": "^7.26.0",
    "babel-plugin-module-resolver": "^5.0.0"
  },
  "private": true,
  "expo": {
    "install": {
      "exclude": [
        "react-native"
      ]
    }
  }
}

lKinderBueno avatar Aug 08 '25 15:08 lKinderBueno