realm / realm-js

Realm is a mobile database: an alternative to SQLite & key-value stores
https://realm.io
Apache License 2.0
5.74k stars 566 forks source link

@realm/react - Cannot access realm that has been closed & Accessing object of type X which has been invalidated or deleted #4746

Open kieran-osgood opened 2 years ago

kieran-osgood commented 2 years ago

How frequently does the bug occur?

All the time

Description

Preface

I've been migrating a react-native app to use the @realm/react (0.3.2) hooks due to some issues like: https://github.com/realm/realm-js/issues/1031 where deleting records is causing crashes with accessing invalidated objects.

This happens when I have a list view which I'm refetching data for and removing objects no longer valid to display anymore, but also happens intermittently in multiple places.

Seems related to #4441 ?

Currently packages:

    "react": "17.0.2",
    "react-native": "0.66.0",
    "realm": "11.0.0-rc.0",

I got onto using this hooks as someone replied to an issue suggesting the @realm/react hooks fixed this issue, so intended to migrate to them so we could get around these realm related crashes.

The bugs

  1. Still getting "accessing invalidated objects" errors when an object becomes invalidated, but I believe the hooks are supposed to return null instead of crashing?

  2. When I try to use useObject from within a component I most times get a crash suggesting that Realm was closed (see stack traces below, first 2 error logs say undefined is not a function on the object, this comes from line 55 in cachedCollection.js const objectId = object._objectId(); - but the third error says Cannot access realm that has been closed.

Happens consistently on a page from a

Is there a way to force keep open Realm - or to explicitly re-open, I thought thats what the createRealmContext/hooks were meant to manage? Perhaps I missed something in the setup though.

I've found whilst navigating to a page and trying to get an object via this fails:

  const cellarDetails = useObject<CellarDetailData>(
    CELLAR_DETAIL_COLLECTION,
    route.params.itemId
  )

If I useQuery and then pick off the item it seems to work fine:

  const collection = useQuery<CellarDetailData>(CELLAR_DETAIL_COLLECTION)
  const cellarDetails = React.useMemo(() => {
    return collection.filtered('code = $0', route.params.itemId)?.[0]
  }, [collection])

Below I've added in some code snippets to give an idea of how I've tried slotting the hooks into our existing ones for a minimal change migration (time constraints).

image

Stacktrace & log output

TypeError: undefined is not a function

This error is located at:
    in Detail (at DetailView/index.tsx:50)
    in RCTView (at View.js:32)
    in View (at styled.tsx:25)
    in Unknown (at Box/index.tsx:100)
    in Box
    in DetailView (at SceneView.tsx:132)
    in StaticContainer
    in EnsureSingleNavigator (at SceneView.tsx:124)
    in SceneView (at useDescriptors.tsx:217)
    in RCTView (at View.js:32)
    in View (at CardContainer.tsx:281)
    in RCTView (at View.js:32)
    in View (at CardContainer.tsx:279)
    in RCTView (at View.js:32)
    in View (at CardSheet.tsx:33)
    in mobile(RootComponent) (at renderApplication.js:60), js engine: hermes

 ERROR  TypeError: undefined is not a function

  Warning: Internal React error: Attempted to capture a commit phase error inside a detached tree. This indicates a bug in React. Likely causes include deleting the same fiber more than once, committing an already-finished tree, or an inconsistent return pointer.

Error message:

Error: Exception in HostFunction: Cannot access realm that has been closed.
    in Cellar (at SceneView.tsx:132)
    in StaticContainer
    in EnsureSingleNavigator (at SceneView.tsx:124)
    in SceneView (at useDescriptors.tsx:217)
    in RCTView (at View.js:32)
    in View (at CardContainer.tsx:281)
    in RCTView (at View.js:32)
    in View (at CardContainer.tsx:279)
    in RCTView (at View.js:32)
    in View (at CardSheet.tsx:33)
    in CardSheet (at Card.tsx:557)
    in RCTView (at View.js:32)
    in View (at createAnimatedComponent.js:242)
    in AnimatedComponent (at createAnimatedComponent.js:295)
    in AnimatedComponentWrapper (at Card.tsx:536)
    in PanGestureHandler (at GestureHandlerNative.tsx:14)

Can you reproduce the bug?

Yes, always

Reproduction Steps

I've replaced our realm provider with the one from: import { createRealmContext } from '@realm/react', this is created in the app.tsx

Our schemas are designed the old style like:

export const OrdersSchema = {
  name: 'OrdersSchema',
  properties: {
    total: 'PriceSchema?',
    orderType: 'string?',
    statusDisplay: 'string?',
    formattedOrderCreationDate: 'string?',
    code: 'string?',
    accountId: 'string?',
    status: 'StatusSchema?',
    displayCPRPurchasePrice: 'bool?',
    placed: 'string?',
  },
  primaryKey: 'code',
}

I tried to just swap out our custom realm hooks like so:

export const useRealmData = <T extends unknown>(
  props: RealmDataProps,
): Realm.Results<T> => {
  const { schema } = props
  const { useQuery } = RealmContext
  return useQuery(schema)
}

export const useRealmDataByID = <T>(
  props: RealmDataProps,
): (Realm.Object & T) | null => {
  const { useObject } = RealmContext
  const { schema, primaryKey } = props

  return useObject(schema, String(primaryKey))
}

Version

realm: 10.20.0-beta.5 - @realm/react: ^0.3.2

What SDK flavour are you using?

Local Database only

Are you using encryption?

No, not using encryption

Platform OS and version(s)

15.5

Build environment

"react": "17.0.2", "react-native": "0.66.0", "realm": "11.0.0-rc.0", "@realm/react": "0.3.2"

hermes: true

Cocoapods version

No response

kneth commented 2 years ago

@kieran-osgood Thank you for reporting. We will investigate it but one question: does it also happen if you upgrade to v11.0.0-rc.0?

kieran-osgood commented 2 years ago

Thanks for checking in! I did try out upgrading to to that rc as well with the same result, probably should've updated the issue with the latest version, will adjust that now

takameyer commented 2 years ago

@kieran-osgood One thing I just noticed, the beta and rc version of Realm are only compatible with specific versions of react-native. This is due to us expanding upon the javascript engine provided by react-native (jsi).
I'm curious to know if upgrading to v11.0.0-rc.1 and running the latest react-native@0.69.3 would cause the issue to go away.

kieran-osgood commented 2 years ago

Interesting, I didn't try rc1 as we're on react native 0.66 and didn't want to commit to migrating to the latest react native version (the crashes I was trying to solve just happen to be a week in advance of us delivering to client for production 😅) - I'll see if I can't find the time to test it and get back to you

Believe this all seems to be related to the accessing deleted / invalidated object errors as we got a lot of them recently which seem impossible to 'catch' from crashing us

For now I've left it with the old method of adding listeners and setting state, with a soft delete property, but I might still have to check out the migrations up as we're finding performance issues with high rerendering of these hooks

takameyer commented 2 years ago

@kieran-osgood I'm curious why the realm was closed. How have you implemented the <RealmProvider> component? Is it wrapping your navigation stack or just particular screens? When <RealmProvider> is unmounted, it will close the realm.
I would be interested in seeing your navigation layout (i assume you are using React Navigation). I can imagine that if the <RealmProvider/> is being unmounted and remounted on navigation, that some errors could occur. It would be advisable to wrap root Navigation component with the <RealmProvider>. Can you provide us more details on how the root level of your application is implemented?

kieran-osgood commented 2 years ago

Yeah I use the RealmProvider component, and so I do get realm working for a while but it consistently crashes when I navigate around so your thought process does sound plausible Below is a snippet from our app.tsx - I've placed it under the redux and query client from react-query providers, but outside of the navigation stacks (thats in Routes)

 <ReactReduxProvider store={store}>
      <QueryClientProvider client={queryClient}>
        <RealmContext.RealmProvider>
          <ApiEmitterProvider>
            <ProductProvider>
              <Routes />
            </ProductProvider>
          </ApiEmitterProvider>
        </RealmContext.RealmProvider>
      </QueryClientProvider>
    </ReactReduxProvider>
takameyer commented 2 years ago

That does look like a valid setup. We will have to investigate further.

rf1804 commented 1 year ago

@takameyer @kneth We are facing same issue and we are using wix react-native-navigation so we have to wrap RealmProvider for each screen Here is how we are doing it

Navigation.registerComponent(
    'Screen1',
    () => props =>
      (
        <Provider store={store}>
          <Screen1 {...props} />
        </Provider>
      ),
    (): any => Screen1,
  );
  Navigation.registerComponent(
    'Screen2',
    () => props =>
      (
        <AppProvider id={realmKey}>
          <UserProvider>
            <RealmProvider>
              <Provider store={store}>
                <Screen2 {...props} />
              </Provider>
            </RealmProvider>
          </UserProvider>
        </AppProvider>
      ),
    (): any => Screen2,
  );
  Navigation.registerComponent(
    'Screen3',
    () => props =>
      (
        <AppProvider id={realmKey}>
          <UserProvider>
            <RealmProvider>
              <Provider store={store}>
                <Screen3 {...props} />
              </Provider>
            </RealmProvider>
          </UserProvider>
        </AppProvider>
      ),
    (): any => Screen3,
  )

As per our internal discussions we figured out either 1 from these 3 should work:-

  1. Realm should not close by navigating to different screen and coming back
  2. There should be a way to pass same realm in all screen.
  3. Even if it is getting closed, how we can open it programatically with some implementation
shridhar48 commented 1 year ago

Hi,

I am facing same problem and below are my dependency versions|

"react-native": "0.68.5", "realm":"11.0.0-rc.0", "@realm/react":"⌃0.4.3"

I have tried wrapping our main navigator component inside RealmProvider and wrapping independent screen inside RealmProvider but still getting same error

In 'object._objectKey()', 'object._objectKey' is undefined

Is there any solution?

bao-multiIT commented 1 year ago

This error also happens to me when i save the code and the app is hot reloaded, even though i didn't delete any of the items Exception in HostFunction: Access to invalidated Results objects, js engine: hermes I'm using RN 0.71.8 with realm ^11.9.0 and @realm/react ^0.4.3

takameyer commented 1 year ago

Sorry for late replys everyone, this has fallen off my radar.

@shridhar48 This is due to using an incompatible version of realm. You need to upgrade to remove this error.

@bao-multiIT Can you make a new issue with some reproduction steps? Your issue could be related to this one, but I want to make sure.

bao-multiIT commented 1 year ago

Hi @takameyer , sorry for the super late reply, i've been very busy lately

I've created a new issue here

takameyer commented 1 year ago

Hey all, we just released @realm/react version 0.6.0 which includes a flag on the RealmProvider called closeOnUnmount. This can be used in multi-realm situations to ensure that all instances of realm are not closed when the RealmProvider goes out of scope. Try it out and let us know if it helps!