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

@realm/react create class-aware hooks #6284

Open
kraenhansen opened this issue Nov 27, 2023 · 3 comments
Open

@realm/react create class-aware hooks #6284

kraenhansen opened this issue Nov 27, 2023 · 3 comments

Comments

@kraenhansen
Copy link
Member

Problem

Users want the simples way possible to query their data, but the current useObject and useQuery APIs take the type as an argument, which could be simplified further.

Additionally, the current 3 positional argument layout of useQuery means users cannot rely on eslint rules to check for missing deps (see #6259).

Solution

A possible solution to both issues above, is to expose functions to generate class / type -aware hooks, which wouldn't require users to pass the type argument:

One example wrapping useQuery (proposed by @kraenhansen)

export const usePersons = createQueryHook<Person>("Person");
export const useDogs = createQueryHook<Dog>("Dog");
// ... and so on, one for every class in their schema

// `createQueryHook` could even have an overload for class-based models, like some of our other APIs:
export const usePersons = createQueryHook(Person);

// And in a consuming component:
const twenty = 20;
const teenagers = usePersons(persons => persons.filtered("age < $0", twenty), [twenty]);

Another example handling both useQuery and useObject (proposed by @takameyer)

import {createModelHooks} from '@realm/react';

// or
import {createModelHooks} from '../MyRealmContext';

class Person extend Realm.Object{} //some class based model

const [usePersonById, usePerson] = createModelHooks(Person);

A third example where the user just have to pass model classes and the hooks are generated for them (proposed by @bimusiek)

enum SchemaName {
    Dog = 'Dog',
    Cat = 'Cat'
}

abstract class RealmModel {
    static schemaName: SchemaName;

    static get() {
        console.log('get');
    }
}

class Dog extends RealmModel {
    static schemaName = SchemaName.Dog

    static woof() {
        console.log('woof');
    }
}

class Cat extends RealmModel {
    static schemaName = SchemaName.Cat

    static meow() {
        console.log('meow')
    }
}

type SchemaWithModels = {
    [SchemaName.Cat]: typeof Cat;
    [SchemaName.Dog]: typeof Dog
};

function createReactUtilities<Model extends typeof RealmModel>(models: Model[]) {
    type SchemaNameHooks = {
        [K in SchemaName as K extends string ? `use${K}` : never]:
        (callback: (query: SchemaWithModels[K]) => void, deps: any[]) => void
    }

    return models.reduce((hooks, model) => {
        return {
            ...hooks,
            [`use${model.schemaName}`]: (callback: any) => {
                callback(model);
            }
        }
    }, {}) as SchemaNameHooks;
}

const utils = createReactUtilities([Dog, Cat]);
utils.useCat((query) => {
    query.get();
    query.meow();
}, [])
utils.useDog((query) => {
    query.get();
    query.woof();
}, [])

Alternatives

Document and add to examples how users could use bind to create derived hooks:

const usePersons = useQuery.bind(null, Person);

How important is this improvement for you?

I would like to have it but have a workaround

Feature would mainly be used with

Atlas Device Sync

@bimusiek
Copy link
Contributor

I was just testing new way of passing an object with type & query as first argument, however it does not work as expected. The rules requires an inline function, not an object with a function inside.

CleanShot 2024-07-26 at 13 31 22@2x

@kraenhansen
Copy link
Member Author

Argh 😞 I don't know how we missed that. This just makes it even more compelling to provide a "class-aware" hook.

@bimusiek
Copy link
Contributor

The class-aware hook would the best, yes. However if you are introducing already new way of using useQuery, why not to allow to pass function as first argument for now?

Also, due to the way our Realm integration is built, we need to have access to createUseQuery where we pass our own useRealm.
Example in PR: #6819

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants