Uploaded image for project: 'Realm JavaScript SDK'
  1. Realm JavaScript SDK
  2. RJS-2649

`@realm/react` create class-aware hooks

      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 https://github.com/realm/realm-js/issues/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)

      Unable to find source-code formatter for language: typescript. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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)

      Unable to find source-code formatter for language: typescript. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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)

      Unable to find source-code formatter for language: typescript. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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

            Assignee:
            Unassigned Unassigned
            Reporter:
            kraen.hansen@mongodb.com Kræn Hansen
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: