Seed Protocol
@seedprotocol/react

Model properties

Hooks to list, load, add and remove the property definitions of a model, for schema editors.

A model property is a field definition on a model, such as title: Text on Post. These hooks return ModelProperty instances from @seedprotocol/sdk. They describe fields; they don't hold item values. For the values stored on an item, see Item properties.

import {
  useModelProperties,
  useModelProperty,
  useCreateModelProperty,
  useDestroyModelProperty,
} from '@seedprotocol/react'

The read hooks wait for the client to be ready before they query. While mounted, each one holds the properties it shows, and when a shown property stops (destroyed, or removed with its model or schema) the hook looks it up again. useModelProperties caches through React Query: outside SeedProvider it uses a shared fallback client with Seed's defaults, and development builds log a warning, so keep it inside SeedProvider (which hooks need it). The ModelProperty class is covered in ModelProperty.

useModelProperties

Lists the property definitions of one model, and updates when properties are added or removed.

function useModelProperties(
  schemaIdOrModelId: string | null | undefined,
  modelName?: string | null | undefined,
): {
  modelProperties: ModelProperty[]
  isLoading: boolean
  error: Error | null
}

Parameters

NameTypeDescription
schemaIdOrModelIdstring | null | undefinedWith modelName: the schema name or schema file ID. Without it: the model's file ID
modelNamestring | null | undefinedThe model's name. Leave it out to look up by model file ID

The model is resolved with useModel, so the same two forms apply.

Returns

FieldTypeDescription
modelPropertiesModelProperty[]The model's properties. [] until loaded
isLoadingbooleantrue only while the list is empty and loading. Background refetches don't set it
errorError | nullThe query's error, if ModelProperty.all() failed

If the first load finds no properties (they can be written a moment after the model), the hook retries after 400 ms, 1.2 s and 2.5 s.

Example

import { useModelProperties } from '@seedprotocol/react'

export function PostFields() {
  const { modelProperties, isLoading, error } = useModelProperties('blog', 'Post')
  if (isLoading) return <p>Loading fields…</p>
  if (error) return <p>{error.message}</p>
  return (
    <table>
      <tbody>
        {modelProperties.map((property) => (
          <tr key={property.name}>
            <td>{property.name}</td>
            <td>{property.dataType}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

useModelProperty

Loads one property definition. It has three call forms.

function useModelProperty(propertyFileId: string | null | undefined): Result
function useModelProperty(
  modelFileId: string | null | undefined,
  propertyName: string | null | undefined,
): Result
function useModelProperty(
  schemaId: string | null | undefined,
  modelName: string | null | undefined,
  propertyName: string | null | undefined,
): Result

type Result = {
  modelProperty: ModelProperty | undefined
  isLoading: boolean
  error: Error | null
}

Parameters

FormArgumentsLooks up
One argumentpropertyFileIdThe property by its file ID
Two argumentsmodelFileId, propertyNameThe named property on the model with that file ID
Three argumentsschemaId, modelName, propertyNameThe named property on that model, in the schema with that name

The number of arguments picks the form. While any of them is null or undefined, the hook loads nothing and returns modelProperty: undefined with isLoading: false, so useModelProperty(schemaId, selectedModel, selectedProperty) waits until a model and a property are selected.

Returns

FieldTypeDescription
modelPropertyModelProperty | undefinedThe property, or undefined if not found or not loaded
isLoadingbooleantrue while the lookup runs
errorError | nullThe lookup error, if any

Only the one-argument form subscribes to the property: it reloads (debounced by 100 ms) whenever the property's state changes. The two- and three-argument forms look the property up once, retry after 400 ms, 1.2 s and 2.5 s while nothing is found, and then update only when the property stops. Use the one-argument form, or useModelProperties, in an editor where the definition can change.

Example

import { useModelProperty } from '@seedprotocol/react'

export function TitleType() {
  const { modelProperty } = useModelProperty('blog', 'Post', 'title')
  return <span>{modelProperty?.dataType}</span>
}

export function PropertyName({ propertyFileId }: { propertyFileId: string }) {
  const { modelProperty, isLoading, error } = useModelProperty(propertyFileId)
  if (error) return <p>{error.message}</p>
  return <span>{isLoading ? '…' : modelProperty?.name}</span>
}

In the three-argument form, schemaId has to be the exact name or schema file ID of a stored schema. When none matches (a typo, or a schema that isn't stored yet), modelProperty is undefined. useCreateModelProperty resolves schemaId the same way.

useCreateModelProperty

Adds a property definition to a model and tracks its progress.

function useCreateModelProperty(): UseCreateModelPropertyReturn

type UseCreateModelPropertyReturn = {
  create: (
    schemaId: string,
    modelName: string,
    property: UseCreateModelPropertyOptions,
  ) => Promise<ModelProperty>
  isLoading: boolean
  error: Error | null
  resetError: () => void
}

type UseCreateModelPropertyOptions = {
  name: string
  dataType: string
  [key: string]: any
}

Parameters of create

NameTypeDescription
schemaIdstringThe exact name or schema file ID of a stored schema, as in useModelProperty
modelNamestringThe model to add the property to
propertyUseCreateModelPropertyOptionsThe definition in the code shape: name, dataType (such as 'Text' or 'Relation'), and any of ref, refValueType, required, validation and the storage fields. name and dataType are required. The property definition reference describes each field

Returns

FieldTypeDescription
createfunctionResolves to the property's instance once the schema name is looked up. The instance can still be loading. Rejects (and sets error) if modelName, property.name or property.dataType is missing, with No stored schema has the name or schema file id "<schemaId>" when no stored schema matches, or with ClientNotInitializedError (code 'CLIENT_NOT_INITIALIZED') before the client's database is open. Call it once the client is ready
isLoadingbooleantrue from the call until the property's status is 'ready' or 'error', or create rejects. A property that's already loaded clears it at once
errorError | nullThe rejection above, or, when the property's load fails, the load's own error (Failed to create model property if it isn't an Error)
resetError() => voidClears error

Example

import { useCreateModelProperty } from '@seedprotocol/react'

export function AddSummaryField() {
  const { create, isLoading, error } = useCreateModelProperty()
  return (
    <>
      <button
        disabled={isLoading}
        onClick={() => create('blog', 'Post', { name: 'summary', dataType: 'Text' }).catch(() => {})}
      >
        Add summary
      </button>
      {error && <p>{error.message}</p>}
    </>
  )
}

useModelProperties('blog', 'Post') picks up the new property once it's written.

create calls ModelProperty.create without waiting for the property to load, and takes no hold, so there's nothing to unload(). If the model already has a property with that name, you get the existing instance back. If the model isn't in the local database, nothing is written: the promise still resolves with the instance, its status turns 'error', and error is set to Failed to create model property.

useDestroyModelProperty

Removes a property definition from its model.

function useDestroyModelProperty(): UseDestroyModelPropertyReturn

type UseDestroyModelPropertyReturn = {
  destroy: (modelProperty: ModelProperty) => Promise<void>
  isLoading: boolean
  error: Error | null
  resetError: () => void
}

Returns

FieldTypeDescription
destroy(modelProperty: ModelProperty) => Promise<void>Calls modelProperty.destroy(). Does nothing if the argument is falsy
isLoadingbooleantrue while destroy runs
errorError | nullSet if destroy() rejects
resetError() => voidClears error

destroy sets error and rejects when modelProperty.destroy() fails, database failures included. Catch the promise. useModelProperties drops the property from its list on its own. Items' stored values for the property are kept; see Other methods.

import type { ModelProperty } from '@seedprotocol/sdk'
import { useDestroyModelProperty } from '@seedprotocol/react'

export function RemoveField({ property }: { property: ModelProperty }) {
  const { destroy, isLoading, error } = useDestroyModelProperty()
  return (
    <>
      <button disabled={isLoading} onClick={() => destroy(property).catch(() => {})}>
        Remove {property.name}
      </button>
      {error && <p>{error.message}</p>}
    </>
  )
}

On this page