prisma / react-native-prisma

Apache License 2.0
150 stars 6 forks source link

Reactive Queries like useFindMany() do not refetch the data on input state change. #28

Open joonshakya opened 2 months ago

joonshakya commented 2 months ago

I was including a reactive state as an input in prisma's reactive query to be able to filter users of a specific type from the database. Even after the useState value changes the data is not fetched with the new input.

Minimal example:

import { Text, TouchableOpacity } from "react-native";
import { prisma } from "../db/prisma";
import { useState } from "react";
import { UserTypeChoice } from "../config/enums";

export default function UsersScreen() {
  const [userType, setUserType] = useState<UserTypeChoice>(
    UserTypeChoice.PARTICIPANT
  );

  const users = prisma.user.useFindMany({
    where: {
      type: userType,
    },
  });

  return (
    <>
      <Text>Users: {users.length}</Text>
      <TouchableOpacity
        onPress={() => {
          setUserType(UserTypeChoice.ORGANIZER);
        }}
      >
        <Text>Organizers</Text>
      </TouchableOpacity>
    </>
  );
}
sorenbs commented 1 month ago

Thank you for reporting this @joonshakya! This is the kind of feedback we are looking for during the preview phase.

There are a few different ways we can handle this.

One approach is to let Prisma take responsibility of the view state as well as the actual app state. I took that approach in this demo app. You can see that addTransactionView is a Prisma model rather than stored with useState: https://github.com/sorenbs/budget-buddy-expo/blob/main/components/AddTransaction.tsx

That's pretty heavy handed though. Perhaps we can make it much simpler to tell Prisma to handle this kind of transient state.

The other approach is to make Prisma aware of relevant useState use. Perhaps a syntax like this:

const [userType, setUserType] = prisma.$watch(useState<UserTypeChoice>(
    UserTypeChoice.PARTICIPANT
  ));

Or perhaps more simply, something like this:

const users = prisma.user.useFindMany({
    where: {
      type: userType,
    },
  }, [userType]);

Do you have any preferences?

CC @betomoedano who also asked about this.

dwightwatson commented 1 month ago

I think passing an array of dependencies feels a lot more natural in React than wrapping useState.

betomoedano commented 1 month ago

I agree with @dwightwatson ☝️

Although, I wonder if we can manage these dependencies internally so that the user does not need to provide the array. In other words, treating the query as a variable that can change. Not only can it change a field but also a property of the query, for example:

const [orderBy, setOrderBy] 
useState<Prisma.NotionFileOrderByWithRelationInput>({ order: "asc" });

const files = extendedClient.notionFile.useFindMany({
  where: { parentFile: { is: null } }, 
  orderBy: orderBy, // property and value will change
});

// Any of these state updates should trigger a re-fetch
setOrderBy({ order: "asc" });
setOrderBy({ createdAt: "desc" });

We also need to prevent re-fetching if query hasn't changed.

ovidb commented 1 month ago

I also agree with @dwightwatson.

Dependency array makes more sense and, implementation wise, is probably easier compared to determining when to re-run the query based on the query inputs.

DX would be so much nicer if Prisma could do the heavy lifting but deps is a good start.