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

useQuery data cannot be used directly in FlatList #5404

Closed
skam22 opened this issue Feb 9, 2023 · 23 comments
Closed

useQuery data cannot be used directly in FlatList #5404

skam22 opened this issue Feb 9, 2023 · 23 comments

Comments

@skam22
Copy link

skam22 commented Feb 9, 2023

How frequently does the bug occur?

Always

Description

this returns an empty FlatList:

const {useQuery} = realmContext;

const App = () => {
  const tasklists = useQuery(Tasklist)
  return (
    <FlatList
      data={tasklists}
      keyExtractor={item => String(item._id}
      renderItem={({item, index}) => <Text>index is: {index}</Text>}
    />
  )

❌ does not work

memoizing the useQuery response also returns an empty FlatList:

const {useQuery} = realmContext;

const App = () => {
  const tasklists = useQuery(Tasklist)
  const sorted = useMemo(() => tasklists.sorted(),[tasklists])
  return (
    <FlatList
      data={sorted}
      keyExtractor={item => String(item._id}
      renderItem={({item, index}) => <Text>index is: {index}</Text>}
    />
  )

❌ does not work

spreading the useQuery response to a new array correctly displays the items in the list:

const {useQuery} = realmContext;

const App = () => {
  const tasklists = useQuery(Tasklist)
  return (
    <FlatList
      data={[...tasklists]}
      keyExtractor={item => String(item._id}
      renderItem={({item, index}) => <Text>index is: {index}</Text>}
    />

✅ this works

mapping through the useQuery response renders correctly as well (but without the benefit of the virtualized list).

const {useQuery} = realmContext;

const App = () => {
  const tasklists = useQuery(Tasklist)
  return (
    <View>
      {tasklists.map((tasklist, index) => <Text key={String(tasklist._id)}>index is: {index}</Text>)}
    </View>

✅ this works

the same source code works fine if pasted into the template starter (react-native.todo.flex) which installs react-native 0.70.5, @realm/react 0.4.1, realm 11.2.0

Stacktrace & log output

No response

Can you reproduce the bug?

Always

Reproduction Steps

No response

Version

realm 11.4.0, @realm/react 0.4.3, react-native 0.71.2

What services are you using?

Atlas Device Sync

Are you using encryption?

Yes

Platform OS and version(s)

IOS and Android

Build environment

realm 11.4.0, @realm/react 0.4.3, react-native 0.71.2

Cocoapods version

No response

@kneth
Copy link
Contributor

kneth commented Feb 12, 2023

I cannot see what the type of item._id is. If it is UUID or ObjectId, it can be that String(item._id) is not producing a unique string. Please try to use item._id.toHexString() instead.

Using the spread operator (data={[...tasklists]}) is likely to give you poor performance as you are copying the Realm objects to JavaScript objects.

@sync-by-unito sync-by-unito bot added the Waiting-For-Reporter Waiting for more information from the reporter before we can proceed label Feb 12, 2023
@skam22
Copy link
Author

skam22 commented Feb 12, 2023

._id is a BSON ObjectId

Using .toHexString() makes no difference.

@github-actions github-actions bot added Needs-Attention Reporter has responded. Review comment. and removed Waiting-For-Reporter Waiting for more information from the reporter before we can proceed labels Feb 12, 2023
@lufke
Copy link

lufke commented Feb 12, 2023

Same problem. The _id is Unique for every item

@skam22
Copy link
Author

skam22 commented Feb 12, 2023

I think I found the issue, the react-native FlatList component _getItemCount looks like this:

  _getItemCount = (data: ?Array<ItemT>): number => {
    if (Array.isArray(data)) {
      const numColumns = numColumnsOrDefault(this.props.numColumns);
      return numColumns > 1 ? Math.ceil(data.length / numColumns) : data.length;
    } else {
      return 0;
    }
  };

Array.isArray(tasklists) is returning false.

The object returned from useQuery fails Array.isArray(), so _getItemCount is returning 0.

@lufke
Copy link

lufke commented Feb 12, 2023

When i see the typo of the useQuery, ir gives me object

@skam22
Copy link
Author

skam22 commented Feb 12, 2023

That’s correct, and an array is an object..

But Array.isArray(useQueryResponseObject) is false.

The object looks like an array, but it isn’t passing the isArray test. I believe isArray looks for the existence of a .toString() method on the object but I could be wrong there.

@skam22
Copy link
Author

skam22 commented Feb 12, 2023

Here is the react-native FlatList commit that breaks the useQuery hook:

facebook/react-native@d574ea3

@skam22
Copy link
Author

skam22 commented Feb 12, 2023

The workaround I’m currently using is patch-package to revert the Array.isArray() guard.


  _getItemCount = (data: ?Array<ItemT>): number => {
    if (data) {
      const numColumns = numColumnsOrDefault(this.props.numColumns);
      return numColumns > 1 ? Math.ceil(data.length / numColumns) : data.length;
    } else {
      return 0;
    }
  };

Moving forward, though, it seems that Realm.Results needs to be updated to pass the data guard inserted with react native 0.71.

@takameyer
Copy link
Contributor

Looks like this was added in 0.71.2. We will need to investigate a solution.

@sync-by-unito sync-by-unito bot removed the Needs-Attention Reporter has responded. Review comment. label Feb 14, 2023
@takameyer
Copy link
Contributor

Just an update. We are currently in discussions with Meta on how to proceed with this matter. We have two options, either they include a way that we can bypass their check, or we create a Proxy object that wraps an Array and access a Realm collection (with a possible performance regression).

@skam22
Copy link
Author

skam22 commented Feb 14, 2023

Just an update. We are currently in discussions with Meta on how to proceed with this matter. We have two options, either they include a way that we can bypass their check, or we create a Proxy object that wraps an Array and access a Realm collection (with a possible performance regression).

Could a third option be to export a FlatList component from the @realm/react package that reverts their check? Then users could import {FlatList} from ‘@realm/react’ instead of from ‘react-native’.

@takameyer
Copy link
Contributor

takameyer commented Feb 15, 2023

@skam22 while possible, I would rather we obtain compatibility with React Native primitives. In any case, we are directly in contact with Meta at this time and hopefully will have a resolution soon.

@hadnet
Copy link

hadnet commented Feb 16, 2023

Just an update. We are currently in discussions with Meta on how to proceed with this matter. We have two options, either they include a way that we can bypass their check, or we create a Proxy object that wraps an Array and access a Realm collection (with a possible performance regression).

Any updates on this issue? First option seems reasonable because Realm is widely used in React Native.

@takameyer
Copy link
Contributor

@hadnet Nothing to report right now, we are still in discussions. That being said, it is a priority for us to resolve this, so we will post as soon as we have an update.

@takameyer
Copy link
Contributor

After discussing this with Meta, they have agreed to loosen the restrictions on the data property to allow for "array-like" structures to be rendered. No current timeline when this will happen, but we are hopeful it will come in a 0.71.x release in the near future. Will keep this open to report and track any progress.

@takameyer
Copy link
Contributor

takameyer commented Feb 22, 2023

The following PRs to react-native will fix this issue:
facebook/react-native#36236 (landing in 0.71.x)
facebook/react-native#36237 (landing in 0.72.x)

Once these are live, we can close this. Thanks everyone for your patience.

@takameyer
Copy link
Contributor

Another update. React Native 0.71.4 is being put together and will fix the issue. Hopefully will happen soon!

@mMarcos208
Copy link

Same issue after upgrade to 0.71.3

  const filteredTasks = useRealmQuery<Task>({
    source: Task.schema.name,
  })
  
    <FlatList
          key={TypeOfCalendar.Card}
          data={filteredTasks}
          keyExtractor={(item) => String(item.id)}
          ListEmptyComponent={<NoRecordsFound />}
          renderItem={({item}) => (
            <TaskList
              onPress={() => goToDetails(item.id)}
            />
          )}
        />
        

@mMarcos208
Copy link

New version is disponible: https://react-native-community.github.io/upgrade-helper/?from=0.71.3&to=0.71.4

Upgrade solve me.

@takameyer
Copy link
Contributor

React Native 0.71.4 is out! If you want FlatList support, then I recommend updating to this version.

@lufke
Copy link

lufke commented Mar 9, 2023

React Native 0.71.4 is out! If you want FlatList support, then I recommend updating to this version.

tested with flatlist and sectionList. Works excelent!!

@skam22
Copy link
Author

skam22 commented Jun 24, 2023 via email

@lufke
Copy link

lufke commented Jun 24, 2023

React native 0.71.3 doesn’t include the changes that meta added. Upgrade to at least 0.71.4

On Jun 22, 2023, at 11:13 AM, lufke wrote:
The problem is back again for FlatList, just created an react native app and must use spread operator on data for see the items on list, otherwise it shows an empty list
“react": "^0.5.0"
"react-native": "0.71.3"
"realm": "^11.10.1"
I can see the items on console.log

I deleted the coment because when paste the dependencies i Saw tjat i was using rn 0.71.3 instead 0.71.4

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants