Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FlatList not updating with React Hooks and Realm

I am writing a custom hook to use it with realm-js.

export default function useRealmResultsHook<T>(query, args): Array<T> {
  const [data, setData] = useState([]);

  useEffect(
    () => {
      function handleChange(newData: Array<T>) {
        // This does not update FlatList, but setData([...newData]) does
        setData(newData);
      }

      const dataQuery = args ? query(...args) : query();
      dataQuery.addListener(handleChange);
      return () => {
        dataQuery.removeAllListeners();
      };
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [query, ...args]
  );

  return data;
}

In my component:

const MyComponent = (props: Props) => {
  const data = useRealmResultsHook(getDataByType, [props.type]);

  return (
    <View>
      <Text>{data.length}</Text>
      <FlatList
        data={data}
        keyExtractor={keyExtractor}
        renderItem={renderItem}
      />
    </View>
  );
};

In the previous component, when doing setData(newData), the data.length gets updated correctly inside the Text. However, the FlatList does not re-render, like the data did not change.

I used a HOC before and a render prop with same the behavior and it was working as expected. Am I doing something wrong? I'd like to avoid cloning data setData([...newData]); because that can be a big amount of it.

Edit 1

Repo to reproduce it

https://github.com/ferrannp/realm-react-native-hooks-stackoverflow

like image 282
Ferran Negre Avatar asked Nov 07 '22 18:11

Ferran Negre


1 Answers

The initial data variable and the newData arg in the handler are the links to the same collection. So they are equal and setData(newData) won’t trigger component’s re-render in this case.

It might be helpful to map Realm collection to the array of items’ ids. So, you will always have the new array in the React state and render will occur properly. It's also useful to check only deletions and insertions of the collection to avoid extra re-renders of the list. But in this case, you should also add listeners to the items.

function useRealmResultsHook(collection) {
  const [data, setData] = useState([]);

  useEffect(
    () => {
      function handleChange(newCollection, changes) {
        if (changes.insertions.length > 0 || changes.deletions.length > 0) {
          setData(newCollection.map(item => item.id));
        }
      }

      collection.addListener(handleChange);
      return () => collection.removeListener(handleChange);
    },
    []
  );

  return data;
}
like image 199
user2281916 Avatar answered Nov 15 '22 05:11

user2281916