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.
Install the React client.@dxos/react-client wraps @dxos/client in a provider and hooks. Configure the bundler as in Installation.
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.
1
/** One peer's copy of the app: the provider gives the hooks below it the peer's client. */
2
exportconst
constPeerApp: ({ 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:
classClient
The Client class encapsulates the core client-side API of DXOS.
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.
Root component that provides the DXOS client instance to child components.
To be used with the useClient hook.
ClientProvider>
6
);
7
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.
1
// Each list is a live query: it re-renders when a record is added, changed by any peer, or removed.
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.
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.
1
/** The record's name, re-rendered whenever any peer renames it. */
2
const
constRecordTitle: ({ 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.
interfaceUnknown
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)
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.
1
/** The options a reference field offers: the records of its target type, by label. */
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.
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.
@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.
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.
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
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
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).
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.
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.
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']>>(
18
(
_db: Database.Database | undefined
_db,
typeUri: URI | undefined
typeUri) =>
const types: (typeof Organization | typeof Person | typeof Event)[]
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.
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.
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
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.
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).
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.
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)
26
.
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.
constinstanceOf: <typeof Person>(schema: typeof Person, value: unknown) => value is Person & Entity.OfKind<EntityKind.Object> (+3overloads)
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.