Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/update-expo-to-51 #539

Closed
wants to merge 38 commits into from

Conversation

enigma0Z
Copy link

@enigma0Z enigma0Z commented Oct 29, 2024

Rationale: to do new local development, current version of Expo Go from iOS app store only supports SDK 51, therefore expo must be updated to 51

Update packages

Additional changes

(TBD until PR is complete)

  • Update Ion icons to not have the platform- prefix (e.g. ios-cog becomes cog)

TODO

  • Fix broken tests
    • ErrorScreen.test.js
      RangeError: Invalid string length
      
    • ServerInput.test.js
          > 26 |      TypeError: Cannot read properties of null (reading 'useReducer')
      
      • Initial research suggests that this is an mobx issue, where decorate() or observe() is triggering hooks badly w/ update expo & updated react
      • My initial research may be a red herring...
    • RefreshWebView.test.js
      RangeError: Invalid string length
      
    • NativeShellWebView.test.js
      RangeError: Invalid string length
      
    • ServerStoreTest.js -> expect(URL).toBeInstanceOf(URL)
      • Honestly IDK what's going on with this one, the error indicates that actual and expected are both URL -- best I can guess it's some object losding issue (like in Java where different classpaths can have the same class but it won't evaluate as the same

@enigma0Z enigma0Z force-pushed the feature/update-expo-to-51 branch 2 times, most recently from 3d55c96 to e02a385 Compare October 29, 2024 19:45
Copy link

@enigma0Z
Copy link
Author

So this error is occurring before any testing actually happens,

  ● ServerInput › should render correctly

    TypeError: Cannot read properties of null (reading 'useReducer')

      25 |
      26 |      it('should render correctly', async () => {
    > 27 |              const { toJSON } = render(
         |                                       ^
      28 |                      <NavigationContainer>
      29 |                              <ServerInput />
      30 |                      </NavigationContainer>

      at useReducer (node_modules/react/cjs/react.development.js:1626:21)
      at useAnimatedProps (node_modules/react-native/Libraries/Animated/useAnimatedProps.js:38:40)
      at node_modules/react-native/Libraries/Animated/createAnimatedComponent.js:35:59
      at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6351:18)
      at updateForwardRef (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9150:20)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11361:16)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15808:12)
      at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15745:5)
      at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15717:7)
      at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15422:20)
      at flushSyncCallbacks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2597:22)
      at flushActQueue (node_modules/react/cjs/react.development.js:2667:24)
      at act (node_modules/react/cjs/react.development.js:2521:11)
      at renderWithAct (node_modules/@testing-library/react-native/build/render.js:68:20)
      at render (node_modules/@testing-library/react-native/build/render.js:46:20)
      at Object.<anonymous> (components/__tests__/ServerInput.test.js:27:28)
      at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:17)
      at _next (node_modules/@babel/runtime/helpers/asyncToGenerator.js:17:9)
      at node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:7
      at Object.<anonymous> (node_modules/@babel/runtime/helpers/asyncToGenerator.js:14:12)

Reviewing detailed test results, this shows up:

  console.error
    Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
    1. You might have mismatching versions of React and the renderer (such as React DOM)
    2. You might be breaking the Rules of Hooks
    3. You might have more than one copy of React in the same app
    See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

      25 |
      26 |      it('should render correctly', async () => {
    > 27 |              const { toJSON } = render(
         |                                       ^
      28 |                      <NavigationContainer>
      29 |                              <ServerInput />
      30 |                      </NavigationContainer>

      at printWarning (node_modules/react/cjs/react.development.js:209:30)
      at error (node_modules/react/cjs/react.development.js:183:7)
      at resolveDispatcher (node_modules/react/cjs/react.development.js:1592:7)
      at useReducer (node_modules/react/cjs/react.development.js:1625:20)
      at useAnimatedProps (node_modules/react-native/Libraries/Animated/useAnimatedProps.js:38:40)
      at node_modules/react-native/Libraries/Animated/createAnimatedComponent.js:35:59
      at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6351:18)
      at updateForwardRef (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9150:20)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11361:16)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15808:12)
      at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15745:5)
      at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15717:7)
      at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15422:20)
      at flushSyncCallbacks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2597:22)
      at flushActQueue (node_modules/react/cjs/react.development.js:2667:24)
      at act (node_modules/react/cjs/react.development.js:2521:11)
      at renderWithAct (node_modules/@testing-library/react-native/build/render.js:68:20)
      at render (node_modules/@testing-library/react-native/build/render.js:46:20)
      at Object.<anonymous> (components/__tests__/ServerInput.test.js:27:28)
      at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:17)
      at _next (node_modules/@babel/runtime/helpers/asyncToGenerator.js:17:9)
      at node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:7
      at Object.<anonymous> (node_modules/@babel/runtime/helpers/asyncToGenerator.js:14:12)

  console.error
    The above error occurred in the <Animated(View)> component:
    
        at /Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/react-native/Libraries/Animated/createAnimatedComponent.js:35:59
        at View
        at Component (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/react-native/jest/mockComponent.js:30:18)
        at Input (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/react-native-elements/dist/input/Input.js:19:19)
        at children (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/react-native-elements/dist/config/withTheme.js:20:17)
        at wrappedComponent (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/mobx-react-lite/lib/observer.js:26:30)
        at children (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/@react-navigation/core/src/EnsureSingleNavigator.tsx:20:49)
        at initialState (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/@react-navigation/core/src/BaseNavigationContainer.tsx:82:7)
        at value (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/@react-navigation/native/src/theming/ThemeProvider.tsx:11:41)
        at theme (/Users/enigma/Source/enigma0z/jellyfin-expo/node_modules/@react-navigation/native/src/NavigationContainer.tsx:55:5)

@enigma0Z
Copy link
Author

This is the part that has me particularly concerned:

    Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
    1. You might have mismatching versions of React and the renderer (such as React DOM)
    2. You might be breaking the Rules of Hooks
    3. You might have more than one copy of React in the same app
    See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

This seems to imply that mismatched versions of react can create this issue, and mobs-react-lite (based on mobx@4|5) is pulling in [email protected]. The conflict is that Expo 51 requires React 18, but Mobx 5.x does not support React 18 -- in fact, Mobx needs to be v9 in order to use React 18. I'm going to explore updating Mobx and potentially replacing mobx-react-lite with mobx-react depending on what breaks next.

@enigma0Z
Copy link
Author

enigma0Z commented Oct 29, 2024

... chasing this down further, it looks like if we update to mobx@6, even though mobx-react-lite seems to be generally fine with things, mobx-sync is not.

Observer decorators (speaking with little experience with mobx here so I'm probably going to get some stuff wrong) were moved into either @annotation style decorators or a makeObservable() call in the class's constructor (from previous instances of a decorate() method). The part I'm confused about is that mobx-sync-lite provided a function, ignore (and likely some others) which do not seem to get along very well with this type of code...

export default class Foo {
  this.property = 'wahtever'
  constructor() {
    makeObservable(this, {
      property: [ ignore, observable ]
    })
  }
}

mobx@6 does not like the [ list, of, things], and neither does it like the ignore function from mobx-sync-lite.

@thornbill -- What does mobx-sync-lite do for the iOS app?

Broken code here if you wanna look... https://github.com/enigma0Z/jellyfin-expo/tree/feature/update-expo-to-15-update-mobx

@enigma0Z
Copy link
Author

So, after even MORE digging, updating Jest / RN testing libraries, I think that the "invalid string length" errors are coming from a circular dependency in a component, as best as I can tell either generated by mobx or caused by uupdating underlying code. All of the "invalid string length"'s stack traces (yay yes the actually have traces now) look like this now.

FAIL  components/__tests__/NativeShellWebView.test.js (19.31 s)
  ● NativeShellWebView › should render correctly

    RangeError: Invalid string length

      39 |              );
      40 |
    > 41 |              expect(rendered_component.toJSON()).toMatchSnapshot();
         |                                                  ^
      42 |      });
      43 | });
      44 |

      at printObjectProperties (node_modules/pretty-format/build/collections.js:170:47)
      at printComplexValue (node_modules/pretty-format/build/index.js:255:50)
      at printer (node_modules/pretty-format/build/index.js:325:10)
      at printObjectProperties (node_modules/pretty-format/build/collections.js:169:21)
      at printComplexValue (node_modules/pretty-format/build/index.js:255:50)
      at printer (node_modules/pretty-format/build/index.js:325:10)
      at printObjectProperties (node_modules/pretty-format/build/collections.js:169:21)
      at printComplexValue (node_modules/pretty-format/build/index.js:255:50)
      at printer (node_modules/pretty-format/build/index.js:325:10)
      (... this repeats for a bit.)

@enigma0Z
Copy link
Author

I would also add that chasing this down is mostly so i can do development on my iPhone, though the tests here are failing, the expo go app apparently works on iPhone

@snordmann snordmann mentioned this pull request Oct 31, 2024
4 tasks
@thornbill
Copy link
Member

Hey @enigma0Z, it is awesome to see you are interested in contributing here! I now have access to a modern Mac for development, so I am no longer blocked from making some of the necessary changes here. 🎉

As you discovered, we really need to deal with the mobx dependency being outdated. For some context mobx-sync-lite is a fork I started of the unmaintained mobx-sync library that manages persisting mobx state. The plan was to update it to support current versions of mobx and strip out features that I thought were unnecessary. As you might have noticed, this really went nowhere due to a lack of time on my part.

In some other projects I started using zustand which maintains official libraries for things like persistence which is really nice... so my current high level plan is:

  1. Bug fix and release the current state of master ASAP to get one last build out that supports iOS 12.
  2. Migrate state management from mobx to zustand.
  3. Upgrade to the latest Expo version.
  4. Possibly drop Expo in favor of just straight React Native.

@enigma0Z
Copy link
Author

enigma0Z commented Nov 7, 2024

Yeah, the expo update has been a bit of a goofy nightmare of weirdly conflicting deps, but I think mobx is at the core of them, mostly that mobx-sync(-lite) wants react 16.x and Expo 51 wants react 18.x. I'll look at zustand of Expo 51 and see if I can maybe front-load some of the migration work. If I get anywhere reasonable you'll see a new PR.

@jellyfin-bot jellyfin-bot added the merge conflict Conflicts prevent merging label Nov 8, 2024
@thornbill thornbill added this to the Future Release milestone Nov 13, 2024
@enigma0Z enigma0Z force-pushed the feature/update-expo-to-51 branch from 3df8129 to 209a2ac Compare December 12, 2024 19:47
@jellyfin-bot jellyfin-bot removed the merge conflict Conflicts prevent merging label Dec 12, 2024
Copy link
Author

@enigma0Z enigma0Z left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being closed; it's based on an out-of-date branch of code and will be superseded by expo 52.

@enigma0Z enigma0Z closed this Dec 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants