Skip to content

React

By the end of this page you will know how the tutorial app’s components get their data, and why they re-render when any peer changes it.

The learning path builds the app’s ECHO code in plain TypeScript: its types, its add and update functions, and its queries. This guide covers the React layer on top, which @dxos/react-client connects to ECHO.

Two rules decide when a component re-renders:

  • A hook subscribes the component to what it returns. useQuery re-renders when the query’s results change, and useObject when the object changes.
  • Reading a field of an ECHO object during render subscribes to nothing. A component that reads person.name straight off the object does not re-render when the name changes.
  1. Install the React client. @dxos/react-client wraps @dxos/client in a provider and hooks. Configure the bundler as in Installation.

    Terminal window
    pnpm add @dxos/react-client
  2. Provide the client. ClientProvider gives the hooks below it a client. The app opens its client and space with openContacts from Installation, and renders one provider per peer.

    /** One peer's copy of the app: the provider gives the hooks below it the peer's client. */
    export const
    const PeerApp: ({ client, space, stage }: {
    client: Client;
    space: Space;
    stage: Stage;
    }) => React.JSX.Element

    One peer's copy of the app: the provider gives the hooks below it the peer's client.

    PeerApp
    = ({
    client: Client
    client
    ,
    space: Space
    space
    ,
    stage: Stage
    stage
    }: {
    client: Client
    client
    :
    class Client

    The Client class encapsulates the core client-side API of DXOS.

    Client
    ;
    space: Space
    space
    :
    (alias) interface Space
    import Space
    Space
    ;
    stage: Stage
    stage
    :
    type Stage = "types" | "records" | "queries" | "complete"

    How far the learning path has built the app: types shows each type's records and its form, records adds creating, editing and removing, queries adds search and a table, and complete adds the graph.

    Stage
    }) => (
    <
    const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
    client?: Client | Provider<Promise<Client>>;
    config?: Config | Provider<Promise<Config>>;
    services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
    fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
    suspend?: boolean;
    noBanner?: boolean;
    onInitialized?: (client: Client) => MaybePromise<void>;
    } & {
    ...;
    } & React.RefAttributes<...>>

    Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

    ClientProvider
    client?: Client | Provider<Promise<Client>> | undefined

    Client object or async provider to enable to caller to do custom initialization.

    NOTE: For advanced use cases only.

    client
    ={
    client: Client
    client
    }>
    <
    const Contacts: ({ space, stage }: {
    space: Space;
    stage: Stage;
    }) => React.JSX.Element

    The tutorial app: one space's organizations, people and events.

    Contacts
    space: Space
    space
    ={
    space: Space
    space
    }
    stage: Stage
    stage
    ={
    stage: Stage
    stage
    } />
    </
    const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
    client?: Client | Provider<Promise<Client>>;
    config?: Config | Provider<Promise<Config>>;
    services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
    fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
    suspend?: boolean;
    noBanner?: boolean;
    onInitialized?: (client: Client) => MaybePromise<void>;
    } & {
    ...;
    } & React.RefAttributes<...>>

    Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

    ClientProvider
    >
    );
  3. Query the space. useQuery takes a database and a query, here the app’s queries from Querying. It returns the current results and re-renders the component when they change.

    // Each list is a live query: it re-renders when a record is added, changed by any peer, or removed.
    const
    const allOrganizations: Entity<Organization & OfKind<EntityKind.Object>>[]
    allOrganizations
    =
    useQuery<Query<Organization & OfKind<EntityKind.Object>>, Entity<Organization & OfKind<EntityKind.Object>>>(resource: Queryable | undefined, query: Query<Organization & OfKind<EntityKind.Object>>): Entity<Organization & OfKind<EntityKind.Object>>[] (+3 overloads)

    Create subscription.

    @param ― queryOrFilter - The query or filter to apply. Query is memoized based on the AST. No need to call useMemo.

    useQuery
    (
    space: Space
    space
    .
    Space.db: EchoDatabase

    Echo database.

    db
    ,
    const organizations: Query<Organization & OfKind<EntityKind.Object>>
    organizations
    );
    const
    const allPeople: Entity<Person & OfKind<EntityKind.Object>>[]
    allPeople
    =
    useQuery<Query<Person & OfKind<EntityKind.Object>>, Entity<Person & OfKind<EntityKind.Object>>>(resource: Queryable | undefined, query: Query<Person & OfKind<EntityKind.Object>>): Entity<Person & OfKind<EntityKind.Object>>[] (+3 overloads)

    Create subscription.

    @param ― queryOrFilter - The query or filter to apply. Query is memoized based on the AST. No need to call useMemo.

    useQuery
    (
    space: Space
    space
    .
    Space.db: EchoDatabase

    Echo database.

    db
    ,
    const people: Query<Person & OfKind<EntityKind.Object>>
    people
    );
    const
    const allEvents: Entity<Event & OfKind<EntityKind.Object>>[]
    allEvents
    =
    useQuery<Query<Event & OfKind<EntityKind.Object>>, Entity<Event & OfKind<EntityKind.Object>>>(resource: Queryable | undefined, query: Query<Event & OfKind<EntityKind.Object>>): Entity<Event & OfKind<EntityKind.Object>>[] (+3 overloads)

    Create subscription.

    @param ― queryOrFilter - The query or filter to apply. Query is memoized based on the AST. No need to call useMemo.

    useQuery
    (
    space: Space
    space
    .
    Space.db: EchoDatabase

    Echo database.

    db
    ,
    const events: Query<Event & OfKind<EntityKind.Object>>
    events
    );

    A query returns the same live object for an id every time, so a renamed record leaves the results array as it was. The app’s list reads its labels with useLabels, which re-renders the list when a label changes.

  4. Show a record. useObject returns a snapshot of the object and re-renders the component when the object changes. The form’s title reads the record’s name through it.

    /** The record's name, re-rendered whenever any peer renames it. */
    const
    const RecordTitle: ({ record }: {
    record: Obj.Unknown;
    }) => React.JSX.Element

    The record's name, re-rendered whenever any peer renames it.

    RecordTitle
    = ({
    record: Obj.Unknown
    record
    }: {
    record: Obj.Unknown
    record
    :
    import Obj
    Obj
    .
    interface Unknown

    Object with no known properties beyond id and kind. Use this when the object's schema/properties are not known. For objects with arbitrary properties, use Obj.AnyProps.

    NOTE: This is a TypeScript type only, not a schema. To validate that a value is an ECHO object, use Schema.is(Type.Obj).

    Runtime Effect schema for any ECHO object. Use for validation, parsing, or as a reference target for collections.

    NOTE: Schema.is(Type.Obj) does STRUCTURAL validation only (checks for id field). Use Obj.isObject() for proper ECHO instance type guards that check the KindId brand.

    @example

    // Structural type guard (accepts any object with id field)
    if (Schema.is(Type.Obj)(unknownValue)) { ... }
    // ECHO instance type guard (checks KindId brand)
    if (Obj.isObject(unknownValue)) { ... }
    // Reference to any object type
    class Collection extends Type.makeObject<Collection>(DXN.make('com.example.type.collection', '0.1.0'))(
    Schema.Struct({ objects: Schema.Array(Ref.Ref(Obj.Unknown)) }),
    ) {}

    Unknown
    }) => {
    const [
    const snapshot: Obj.Snapshot<Obj.Unknown>
    snapshot
    ] =
    useObject<Obj.Unknown>(obj: Obj.Unknown): [Obj.Snapshot<Obj.Unknown>, ObjectUpdateCallback<Obj.Unknown>] (+9 overloads)

    Hook to subscribe to an entire Echo object. Returns a snapshot of the current object value and automatically re-renders when the object changes.

    @param ― obj - The Echo object to subscribe to (objects only, not relations)

    @returns ― The current object snapshot and update callback

    useObject
    (
    record: Obj.Unknown
    record
    );
    const
    const label: unknown
    label
    = 'title' in
    const snapshot: Obj.Snapshot<Obj.Unknown>
    snapshot
    ?
    const snapshot: Omit<Obj.Unknown, "~@dxos/echo/Kind"> & BaseSnapshot & Record<"title", unknown>
    snapshot
    .
    title: unknown
    title
    : 'name' in
    const snapshot: Obj.Snapshot<Obj.Unknown>
    snapshot
    ?
    const snapshot: Omit<Obj.Unknown, "~@dxos/echo/Kind"> & BaseSnapshot & Record<"name", unknown>
    snapshot
    .
    name: unknown
    name
    :
    var undefined
    undefined
    ;
    return <
    JSX.IntrinsicElements.h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
    h3
    HTMLAttributes<HTMLHeadingElement>.className?: string | undefined
    className
    ='text-lg font-medium'>{
    var String: StringConstructor
    (value?: any) => string

    Allows manipulation and formatting of text strings and determination and location of substrings within strings.

    String
    (
    const label: unknown
    label
    ?? '')}</
    JSX.IntrinsicElements.h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
    h3
    >;
    };
  5. Edit with a form built from the schema. Form.Root from @dxos/react-ui-form takes the type’s schema, so a new field in the schema is a new field in the form. A reference field lists the records of the type it points at.

    /** The options a reference field offers: the records of its target type, by label. */
    const
    const getOptions: (objects: Entity.Any[], options?: {
    parentLabel?: boolean;
    getTypePlaceholder?: (typename: string) => string;
    }) => RefOption[]

    The options a reference field offers: the records of its target type, by label.

    getOptions
    =
    useCallback<(objects: Entity.Any[], options?: {
    parentLabel?: boolean;
    getTypePlaceholder?: (typename: string) => string;
    }) => RefOption[]>(callback: (objects: Entity.Any[], options?: {
    parentLabel?: boolean;
    getTypePlaceholder?: (typename: string) => string;
    }) => RefOption[], deps: React.DependencyList): (objects: Entity.Any[], options?: {
    parentLabel?: boolean;
    getTypePlaceholder?: (typename: string) => string;
    }) => RefOption[]

    useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

    useCallback
    <
    type NonNullable<T> = T & {}

    Exclude null and undefined from T

    NonNullable
    <
    type RefFieldDataProps = {
    useType?: (db?: Database.Database, typeUri?: URI) => Type.AnyEntity;
    useResults?: (db?: Database.Database, typename?: string) => Entity.Any[];
    getOptions?: (objects: Entity.Any[], options?: {
    parentLabel?: boolean;
    getTypePlaceholder?: (typename: string) => string;
    }) => RefOption[];
    onCreate?: (schema: Type.AnyEntity, values: any) => Obj.Unknown | Promise<Obj.Unknown> | undefined | void;
    getCreateDefaults?: (props: {
    jsonPath: string;
    schema: Type.AnyEntity;
    }) => Record<string, unknown> | undefined;
    resolveCreateEntry?: (typename: string) => CreateEntryOverride | undefined;
    }

    Data plumbing for resolving and creating ref targets: how to look up the ref type, query candidate objects, map them to options, and persist a newly-created one.

    RefFieldDataProps
    ['getOptions']>>(
    (
    results: Entity.Any[]
    results
    ) =>
    [...
    results: Entity.Any[]
    results
    ]
    .
    Array<Any>.sort(compareFn?: ((a: Entity.Any, b: Entity.Any) => number) | undefined): Entity.Any[]

    Sorts an array in place. This method mutates the array and returns a reference to the same array.

    @param ―

    compareFn Function used to determine the order of the elements. It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive value otherwise. If omitted, the elements are sorted in ascending, UTF-16 code unit order.

    [11,2,22,1].sort((a, b) => a - b)

    sort
    ((
    a: Entity.Any
    a
    ,
    b: Entity.Any
    b
    ) => (
    import Entity
    Entity
    .
    const getLabel: (entity: Entity.Unknown | Entity.Snapshot, options?: GetLabelOptions) => string | undefined

    Get the label of an entity.

    @param ― options.fallback 'typename' returns the entity's typename when no label is set (e.g. org.dxos.type.table).

    @performance ― O(label accessors); reads the fields named by the schema LabelAnnotation.

    getLabel
    (
    a: Entity.Any
    a
    ) ?? '').
    String.localeCompare(that: string, locales?: Intl.LocalesArgument, options?: Intl.CollatorOptions): number (+2 overloads)

    Determines whether two strings are equivalent in the current or specified locale.

    @param ― that String to compare to target string

    @param ― locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.

    @param ― options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.

    localeCompare
    (
    import Entity
    Entity
    .
    const getLabel: (entity: Entity.Unknown | Entity.Snapshot, options?: GetLabelOptions) => string | undefined

    Get the label of an entity.

    @param ― options.fallback 'typename' returns the entity's typename when no label is set (e.g. org.dxos.type.table).

    @performance ― O(label accessors); reads the fields named by the schema LabelAnnotation.

    getLabel
    (
    b: Entity.Any
    b
    ) ?? ''))
    .
    Array<Any>.map<{
    icon?: string | undefined;
    id: URI;
    label: string;
    }>(callbackfn: (value: Entity.Any, index: number, array: Entity.Any[]) => {
    icon?: string | undefined;
    id: URI;
    label: string;
    }, thisArg?: any): {
    icon?: string | undefined;
    id: URI;
    label: string;
    }[]

    Calls a defined callback function on each element of an array, and returns an array that contains the results.

    @param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

    map
    ((
    result: Entity.Any
    result
    ) => {
    const
    const target: typeof Organization | typeof Person | typeof Event | undefined
    target
    =
    const types: (typeof Organization | typeof Person | typeof Event)[]
    types
    .
    Array<typeof Organization | typeof Person | typeof Event>.find(predicate: (value: typeof Organization | typeof Person | typeof Event, index: number, obj: (typeof Organization | typeof Person | typeof Event)[]) => unknown, thisArg?: any): typeof Organization | typeof Person | typeof Event | undefined (+1 overload)

    Returns the value of the first element in the array where predicate is true, and undefined otherwise.

    @param ― predicate find calls predicate once for each element of the array, in ascending order, until it finds one where predicate returns true. If such an element is found, find immediately returns that element value. Otherwise, find returns undefined.

    @param ― thisArg If provided, it will be used as the this value for each invocation of predicate. If it is not provided, undefined is used instead.

    find
    ((
    type: typeof Organization | typeof Person | typeof Event
    type
    ) =>
    import Type
    Type
    .
    const getTypename: (input: Type.AnyEntity) => string

    @returns ―

    The typename. Example: com.example.type.person.

    Persisted Type.Type entities carry typename in EntityMeta.key (the canonical registry-provenance field); unnamed drafts fall back to the entity's object id so the helper always returns a string. Any dxn: or echo:/ prefix is stripped — typename is a bare identifier, not a URI.

    @performance ― O(1) meta read with jsonSchema and id fallbacks.

    getTypename
    (
    type: typeof Organization | typeof Person | typeof Event
    type
    ) ===
    import Entity
    Entity
    .
    const getTypename: (entity: Entity.AnyInput) => string | undefined

    Get the typename of an entity's type. For object/relation instances this is the typename of the type they were created from; for a type entity it is the type's own typename (e.g. com.example.type.person).

    @performance ― O(1); reads the schema type annotation (database objects resolve the schema by registry URI lookup).

    getTypename
    (
    result: Entity.Any
    result
    ));
    return {
    id: URI
    id
    :
    import Entity
    Entity
    .
    const getURI: (entity: Entity.AnyInput, options?: Obj.GetURIOptions) => URI

    Get the canonical URI of an entity (object, relation, or type). Returns URI.URI — an EID for object/relation instances and persisted types, or a typename DXN for static type entities; narrow with EID.parse(uri) or DXN.tryMake(uri) at the point of use.

    @param ― options.prefer - Controls the URI form (see internal.GetURIOptions).

    @performance ― O(1); returns the stored URI, constructing (and allocating) one only when options.prefer asks for another form.

    getURI
    (
    result: Entity.Any
    result
    ),
    label: string
    label
    :
    import Entity
    Entity
    .
    const getLabel: (entity: Entity.Unknown | Entity.Snapshot, options?: GetLabelOptions) => string | undefined

    Get the label of an entity.

    @param ― options.fallback 'typename' returns the entity's typename when no label is set (e.g. org.dxos.type.table).

    @performance ― O(label accessors); reads the fields named by the schema LabelAnnotation.

    getLabel
    (
    result: Entity.Any
    result
    ) ??
    result: Entity.Any
    result
    .
    OfKind<EntityKind>.id: string
    id
    ,
    ...(
    const target: typeof Organization | typeof Person | typeof Event | undefined
    target
    && {
    icon?: string | undefined
    icon
    :
    function getTypeIcon(type: Type.AnyObj): IconAnnotation | undefined

    The glyph and hue a type declares on its schema.

    getTypeIcon
    (
    const target: typeof Organization | typeof Person | typeof Event
    target
    )?.
    icon: string | undefined

    Sprite icon name (e.g., 'ph--user--regular', 'ph--cube--regular', 'px--anthropic--regular').

    ph--* is Phosphor; px--* and dx--* are brand glyphs, which are sprite-only and carry no weight variants. All three are admitted because a type whose subject IS a brand — an Anthropic session, a GitHub repo — has no honest Phosphor equivalent.

    icon
    }),
    };
    }),
    [],
    );
    const
    const useRefType: (db?: Database.Database, typeUri?: URI) => Type.AnyEntity
    useRefType
    =
    useCallback<(db?: Database.Database, typeUri?: URI) => Type.AnyEntity>(callback: (db?: Database.Database, typeUri?: URI) => Type.AnyEntity, deps: React.DependencyList): (db?: Database.Database, typeUri?: URI) => Type.AnyEntity

    useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

    useCallback
    <
    type NonNullable<T> = T & {}

    Exclude null and undefined from T

    NonNullable
    <
    type RefFieldDataProps = {
    useType?: (db?: Database.Database, typeUri?: URI) => Type.AnyEntity;
    useResults?: (db?: Database.Database, typename?: string) => Entity.Any[];
    getOptions?: (objects: Entity.Any[], options?: {
    parentLabel?: boolean;
    getTypePlaceholder?: (typename: string) => string;
    }) => RefOption[];
    onCreate?: (schema: Type.AnyEntity, values: any) => Obj.Unknown | Promise<Obj.Unknown> | undefined | void;
    getCreateDefaults?: (props: {
    jsonPath: string;
    schema: Type.AnyEntity;
    }) => Record<string, unknown> | undefined;
    resolveCreateEntry?: (typename: string) => CreateEntryOverride | undefined;
    }

    Data plumbing for resolving and creating ref targets: how to look up the ref type, query candidate objects, map them to options, and persist a newly-created one.

    RefFieldDataProps
    ['useType']>>(
    (
    _db: Database.Database | undefined
    _db
    ,
    typeUri: URI | undefined
    typeUri
    ) =>
    const types: (typeof Organization | typeof Person | typeof Event)[]
    types
    .
    Array<typeof Organization | typeof Person | typeof Event>.find(predicate: (value: typeof Organization | typeof Person | typeof Event, index: number, obj: (typeof Organization | typeof Person | typeof Event)[]) => unknown, thisArg?: any): typeof Organization | typeof Person | typeof Event | undefined (+1 overload)

    Returns the value of the first element in the array where predicate is true, and undefined otherwise.

    @param ― predicate find calls predicate once for each element of the array, in ascending order, until it finds one where predicate returns true. If such an element is found, find immediately returns that element value. Otherwise, find returns undefined.

    @param ― thisArg If provided, it will be used as the this value for each invocation of predicate. If it is not provided, undefined is used instead.

    find
    ((
    type: typeof Organization | typeof Person | typeof Event
    type
    ) =>
    var String: StringConstructor
    (value?: any) => string

    Allows manipulation and formatting of text strings and determination and location of substrings within strings.

    String
    (
    typeUri: URI | undefined
    typeUri
    ).
    String.includes(searchString: string, position?: number): boolean

    Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.

    @param ― searchString search string

    @param ― position If position is undefined, 0 is assumed, so as to search all of the String.

    includes
    (
    import Type
    Type
    .
    const getTypename: (input: Type.AnyEntity) => string

    @returns ―

    The typename. Example: com.example.type.person.

    Persisted Type.Type entities carry typename in EntityMeta.key (the canonical registry-provenance field); unnamed drafts fall back to the entity's object id so the helper always returns a string. Any dxn: or echo:/ prefix is stripped — typename is a bare identifier, not a URI.

    @performance ― O(1) meta read with jsonSchema and id fallbacks.

    getTypename
    (
    type: typeof Organization | typeof Person | typeof Event
    type
    ))) ??
    const recordType: typeof Organization | typeof Person | Type.AnyObj | typeof Event
    recordType
    ,
    [
    const recordType: typeof Organization | typeof Person | Type.AnyObj | typeof Event
    recordType
    ],
    );

    On save, the form hands back its values, and the app writes them with the functions from Mutating objects.

    const
    const handleSave: (next: Record<string, unknown>) => void
    handleSave
    =
    useCallback<(next: Record<string, unknown>) => void>(callback: (next: Record<string, unknown>) => void, deps: React.DependencyList): (next: Record<string, unknown>) => void

    useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

    useCallback
    (
    (
    next: Record<string, unknown>
    next
    :
    type Record<K extends keyof any, T> = { [P in K]: T; }

    Construct a type with a set of properties K of type T

    Record
    <string, unknown>) => {
    if (
    record: Obj.Unknown | undefined
    record
    ) {
    function updateRecord(record: Obj.Unknown, values: Record<string, unknown>): Obj.Unknown

    Writes the values a form returns onto a record, one field at a time.

    updateRecord
    (
    record: Obj.Unknown
    record
    ,
    next: Record<string, unknown>
    next
    );
    onDone: (() => void) | undefined
    onDone
    ?.();
    return;
    }
    // A reference field reports the URI of its choice; the record behind it is what the add functions take.
    const
    const resolve: (value: unknown) => Obj.Unknown | undefined
    resolve
    = (
    value: unknown
    value
    : unknown) =>
    import Ref
    Ref
    .
    const isRef: (value: unknown) => value is Ref.Unknown

    Type guard for refs.

    @performance ― O(1) brand check; no allocation.

    isRef
    (
    value: unknown
    value
    ) ?
    records: readonly Obj.Unknown[]

    Every record, which a reference field's choice is resolved against.

    records
    .
    ReadonlyArray<Unknown>.find(predicate: (value: Obj.Unknown, index: number, obj: readonly Obj.Unknown[]) => unknown, thisArg?: any): Obj.Unknown | undefined (+1 overload)

    Returns the value of the first element in the array where predicate is true, and undefined otherwise.

    @param ― predicate find calls predicate once for each element of the array, in ascending order, until it finds one where predicate returns true. If such an element is found, find immediately returns that element value. Otherwise, find returns undefined.

    @param ― thisArg If provided, it will be used as the this value for each invocation of predicate. If it is not provided, undefined is used instead.

    find
    ((
    candidate: Obj.Unknown
    candidate
    ) =>
    import Entity
    Entity
    .
    const getURI: (entity: Entity.AnyInput, options?: Obj.GetURIOptions) => URI

    Get the canonical URI of an entity (object, relation, or type). Returns URI.URI — an EID for object/relation instances and persisted types, or a typename DXN for static type entities; narrow with EID.parse(uri) or DXN.tryMake(uri) at the point of use.

    @param ― options.prefer - Controls the URI form (see internal.GetURIOptions).

    @performance ― O(1); returns the stored URI, constructing (and allocating) one only when options.prefer asks for another form.

    getURI
    (
    candidate: Obj.Unknown
    candidate
    ) ===
    value: Ref.Unknown
    value
    .
    Ref<Unknown>.uri: URI

    Target URI (either an echo: EID for an object reference or a dxn: DXN for a type reference).

    @performance ― O(1) field read.

    uri
    ) :
    var undefined
    undefined
    ;
    if (
    const recordType: typeof Organization | typeof Person | Type.AnyObj | typeof Event
    recordType
    ===
    class Person
    Person
    ) {
    const
    const employer: Obj.Unknown | undefined
    employer
    =
    const resolve: (value: unknown) => Obj.Unknown | undefined
    resolve
    (
    next: Record<string, unknown>
    next
    .
    unknown
    employer
    );
    function addPerson(db: Database.Database, { employer, ...props }: {
    name: string;
    email?: string;
    employer?: Organization;
    }): BaseObj & Person & Entity.OfKind<EntityKind.Object>
    addPerson
    (
    db: Database.Database
    db
    , {
    name: string
    name
    :
    var String: StringConstructor
    (value?: any) => string

    Allows manipulation and formatting of text strings and determination and location of substrings within strings.

    String
    (
    next: Record<string, unknown>
    next
    .
    unknown
    name
    ?? ''),
    email?: string | undefined
    email
    :
    const optional: (value: unknown) => string | undefined

    Text out of a form value, or nothing for a blank field.

    optional
    (
    next: Record<string, unknown>
    next
    .
    unknown
    email
    ),
    employer?: Organization | undefined
    employer
    :
    const employer: Obj.Unknown | undefined
    employer
    &&
    import Obj
    Obj
    .
    const instanceOf: <typeof Organization>(schema: typeof Organization, value: unknown) => value is Organization & Entity.OfKind<EntityKind.Object> (+3 overloads)
    instanceOf
    (
    class Organization
    Organization
    ,
    const employer: Obj.Unknown
    employer
    ) ?
    const employer: Organization & Entity.OfKind<EntityKind.Object>
    employer
    :
    var undefined
    undefined
    ,
    });
    } else if (
    const recordType: typeof Organization | typeof Person | Type.AnyObj | typeof Event
    recordType
    ===
    class Event
    Event
    ) {
    function addEvent(db: Database.Database, { attendees, ...props }: {
    title: string;
    description?: string;
    attendees?: Person[];
    }): BaseObj & Event & Entity.OfKind<EntityKind.Object>
    addEvent
    (
    db: Database.Database
    db
    , {
    title: string
    title
    :
    var String: StringConstructor
    (value?: any) => string

    Allows manipulation and formatting of text strings and determination and location of substrings within strings.

    String
    (
    next: Record<string, unknown>
    next
    .
    unknown
    title
    ?? ''),
    description?: string | undefined
    description
    :
    const optional: (value: unknown) => string | undefined

    Text out of a form value, or nothing for a blank field.

    optional
    (
    next: Record<string, unknown>
    next
    .
    unknown
    description
    ),
    attendees?: Person[] | undefined
    attendees
    : (
    var Array: ArrayConstructor
    Array
    .
    ArrayConstructor.isArray(arg: any): arg is any[]
    isArray
    (
    next: Record<string, unknown>
    next
    .
    unknown
    attendees
    ) ?
    next: Record<string, unknown>
    next
    .
    any[]
    attendees
    : [])
    .
    Array<any>.map<Obj.Unknown | undefined>(callbackfn: (value: any, index: number, array: any[]) => Obj.Unknown | undefined, thisArg?: any): (Obj.Unknown | undefined)[]

    Calls a defined callback function on each element of an array, and returns an array that contains the results.

    @param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

    map
    (
    const resolve: (value: unknown) => Obj.Unknown | undefined
    resolve
    )
    .
    Array<Unknown | undefined>.filter<Obj.Unknown>(predicate: (value: Obj.Unknown | undefined, index: number, array: (Obj.Unknown | undefined)[]) => value is Obj.Unknown, thisArg?: any): Obj.Unknown[] (+1 overload)

    Returns the elements of an array that meet the condition specified in a callback function.

    @param ― predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.

    filter
    ((
    candidate: Obj.Unknown | undefined
    candidate
    ) =>
    candidate: Obj.Unknown | undefined
    candidate
    !==
    var undefined
    undefined
    )
    .
    Array<Unknown>.filter<Person & Entity.OfKind<EntityKind.Object>>(predicate: (value: Obj.Unknown, index: number, array: Obj.Unknown[]) => value is Person & Entity.OfKind<EntityKind.Object>, thisArg?: any): (Person & Entity.OfKind<EntityKind.Object>)[] (+1 overload)

    Returns the elements of an array that meet the condition specified in a callback function.

    @param ― predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.

    @param ― thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.

    filter
    ((
    candidate: Obj.Unknown
    candidate
    ) =>
    import Obj
    Obj
    .
    const instanceOf: <typeof Person>(schema: typeof Person, value: unknown) => value is Person & Entity.OfKind<EntityKind.Object> (+3 overloads)
    instanceOf
    (
    class Person
    Person
    ,
    candidate: Obj.Unknown
    candidate
    )),
    });
    } else {
    function addOrganization(db: Database.Database, props: {
    name: string;
    website?: string;
    }): BaseObj & Organization & Entity.OfKind<EntityKind.Object>
    addOrganization
    (
    db: Database.Database
    db
    , {
    name: string
    name
    :
    var String: StringConstructor
    (value?: any) => string

    Allows manipulation and formatting of text strings and determination and location of substrings within strings.

    String
    (
    next: Record<string, unknown>
    next
    .
    unknown
    name
    ?? ''),
    website?: string | undefined
    website
    :
    const optional: (value: unknown) => string | undefined

    Text out of a form value, or nothing for a blank field.

    optional
    (
    next: Record<string, unknown>
    next
    .
    unknown
    website
    ) });
    }
    onDone: (() => void) | undefined
    onDone
    ?.();
    },
    [
    db: Database.Database
    db
    ,
    record: Obj.Unknown | undefined
    record
    ,
    records: readonly Obj.Unknown[]

    Every record, which a reference field's choice is resolved against.

    records
    ,
    const recordType: typeof Organization | typeof Person | Type.AnyObj | typeof Event
    recordType
    ,
    onDone: (() => void) | undefined
    onDone
    ],
    );

The app with every piece in place: lists, the table and the graph are all useQuery, and the form is useObject and Form.Root.

fallback renders while the client initializes.

const
const Loading: () => React.JSX.Element
Loading
= () => <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>Loading…</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>;
const
const WithFallback: () => React.JSX.Element
WithFallback
= () => (
<
const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
client?: Client | Provider<Promise<Client>>;
config?: Config | Provider<Promise<Config>>;
services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
suspend?: boolean;
noBanner?: boolean;
onInitialized?: (client: Client) => MaybePromise<void>;
} & {
...;
} & React.RefAttributes<...>>

Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

ClientProvider
types?: Type.AnyEntity[] | undefined

ECHO schema.

types
={[
class Task
Task
]}
fallback?: React.FunctionComponent<Partial<ClientContextProps>> | undefined

ReactNode to display until the client is available.

fallback
={
const Loading: () => React.JSX.Element
Loading
}>
<
const Tasks: () => React.JSX.Element | null
Tasks
/>
</
const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
client?: Client | Provider<Promise<Client>>;
config?: Config | Provider<Promise<Config>>;
services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
suspend?: boolean;
noBanner?: boolean;
onInitialized?: (client: Client) => MaybePromise<void>;
} & {
...;
} & React.RefAttributes<...>>

Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

ClientProvider
>
);

Create the client yourself to use it outside React too.

const
const client: Client
client
= new
new Client(options?: ClientOptions): Client

The Client class encapsulates the core client-side API of DXOS.

Client
({
types?: Type.AnyEntity[] | undefined

ECHO schema.

types
: [
class Task
Task
] });
const
const WithClient: () => React.JSX.Element
WithClient
= () => (
<
const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
client?: Client | Provider<Promise<Client>>;
config?: Config | Provider<Promise<Config>>;
services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
suspend?: boolean;
noBanner?: boolean;
onInitialized?: (client: Client) => MaybePromise<void>;
} & {
...;
} & React.RefAttributes<...>>

Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

ClientProvider
client?: Client | Provider<Promise<Client>> | undefined

Client object or async provider to enable to caller to do custom initialization.

NOTE: For advanced use cases only.

client
={
const client: Client
client
}>
<
const Tasks: () => React.JSX.Element | null
Tasks
/>
</
const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
client?: Client | Provider<Promise<Client>>;
config?: Config | Provider<Promise<Config>>;
services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
suspend?: boolean;
noBanner?: boolean;
onInitialized?: (client: Client) => MaybePromise<void>;
} & {
...;
} & React.RefAttributes<...>>

Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

ClientProvider
>
);

config takes a function that returns a Config, built from the same sources as in Configure the client.

const
const WithConfig: () => React.JSX.Element
WithConfig
= () => (
<
const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
client?: Client | Provider<Promise<Client>>;
config?: Config | Provider<Promise<Config>>;
services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
suspend?: boolean;
noBanner?: boolean;
onInitialized?: (client: Client) => MaybePromise<void>;
} & {
...;
} & React.RefAttributes<...>>

Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

ClientProvider
types?: Type.AnyEntity[] | undefined

ECHO schema.

types
={[
class Task
Task
]}
config?: Config | Provider<Promise<Config>> | undefined

Config object or async provider.

NOTE: If a client is provided then config is ignored.

config
={async () => new
new Config(config?: ConfigInit, ...objects: ConfigInit[]): Config

Creates an immutable instance.

@constructor

Config
(
function Local(): ConfigInit

Development config.

Local
(),
function Defaults(basePath?: string): ConfigInit

JSON config.

Defaults
())}>
<
const Tasks: () => React.JSX.Element | null
Tasks
/>
</
const ClientProvider: React.ForwardRefExoticComponent<Omit<ClientOptions, "config" | "services"> & Pick<ClientContextProps, "status"> & {
client?: Client | Provider<Promise<Client>>;
config?: Config | Provider<Promise<Config>>;
services?: ClientServicesProvider | ((config?: Config) => MaybePromise<ClientServicesProvider>);
fallback?: React.FunctionComponent<Partial<ClientContextProps>>;
suspend?: boolean;
noBanner?: boolean;
onInitialized?: (client: Client) => MaybePromise<void>;
} & {
...;
} & React.RefAttributes<...>>

Root component that provides the DXOS client instance to child components. To be used with the useClient hook.

ClientProvider
>
);

You can provide a client to a React app, query a space from a component, show a record that re-renders on every change, and edit records with a form built from their schema. The Tasks sample is another complete app built this way.

Spaces and databases: what a space is, and how peers join one.