Skip to content

Commit

Permalink
docs(state-query): add recs to new docs (#1917)
Browse files Browse the repository at this point in the history
  • Loading branch information
qbzzt authored Nov 13, 2023
1 parent 5fd0e2e commit 82d62a8
Showing 1 changed file with 133 additions and 0 deletions.
133 changes: 133 additions & 0 deletions next-docs/pages/state-query/typescript/recs.mdx
Original file line number Diff line number Diff line change
@@ -1 +1,134 @@
# RECS

The recs package is a way to query the [Store](/store/introduction) and react to changes.

There are two APIs to read data and react to changes with `recs`:

- [Reading component value directly](#reading-component-value-directly).
- [Subscribing to a query that returns a set of matching entities](#running-queries).

For the examples below, let's assume this MUD config. Notice that each of these tables has an (implicit) `bytes32` key.

```typescript
const config = mudConfig({
tables: {
NameComponent: "string",
PlayerComponent: "bool",
PositionComponent: { valueSchema: { x: "int32", y: "int32" } },
},
});
```

## Reading component value directly

If you have a specific key and a reference to a component, you can query its value directly, or use a React hook that will re-render when the corresponding component value updates.

### Reading component value in vanilla javascript

```typescript
import { getComponentValueStrict } from "@latticexyz/recs";
// note: NameComponent is an recs component; components could come from the `setup` function of MUD.
const { NameComponent } = components
// get a reference to the recs world; as an example from the `setup` function of MUD.
const world = [...]
// you need a reference to an `Entity` from recs. For the sake of the example, let's say we know that the bytes32 key is "0xDEAD"
// you wouldn't do normally: entities would be found via queries.
const entityID = "0xDEAD" as EntityID;
const entity = world.registerEntity({ id: "0xDEAD" as EntityI })
// now we can fetch the value of the NameComponent on our entity
const name = getComponentValueStrict(NameComponent, entity)
// -> "John Doe"
```

### Reading component value in react with `@latticexyz/react`

```tsx
import { useComponentValue } from "@latticexyz/react";
function ExampleComponent() {
const { components, world } = useMUD;
const { NameComponent } = components;
// you need a reference to an `Entity` from recs. For the sake of the example, let's say we know that the bytes32 key is "0xDEAD"
// you wouldn't do this normally: entities would be found via queries.
const entityID = "0xDEAD" as EntityID;
const entity = world.registerEntity({ id: "0xDEAD" });
// now we can fetch the value of the NameComponent on our entity
const name = useComponentValue(NameComponent, entity);
// -> "John Doe"
// this will re-render when the component value changes!
return <p>{name ?? "<no name>"}</p>;
}
```

## Running queries

A common way of working with ECS data is by using queries.
A query is a function that returns a list of entities that match a set of predicates called _query fragments_.

There are 4 types of query fragments:

1. `Has`: matches entities that have a specific component.
2. `Not`: matches entities that do not have a specific component.
3. `HasValue`: matches entities that have a specific component with a set value.
4. `NotValue`: matches entities that do not have a component with a set value (will also match if it doesn't have the component at all)

### Running query with vanilla javascript

```typescript
import { runQuery, Has, HasValue, getComponentValueStrict } from "@latticexyz/recs";
const { PlayerComponent, PositionComponent, NameComponent } = components
// query for all named players at the center of the universe
const matchingEntities = runQuery([
Has(PlayerComponent),
Has(Name),
HasValue(PositionCompoennt, {x: 0, y: 0})
])
// now you can map these to their name as an example
const names = matchingEntities.map(
playerEntity => getComponentValueStrict(NameComponent, playerEntity
)
// -> ["Bob", "Alice", "Eve"]
```
### Reacting to queries with vanilla javascript
```typescript
import { defineSystem, Has, HasValue, getComponentValueStrict, UpdateType } from "@latticexyz/recs";
const { PlayerComponent, PositionComponent, NameComponent } = components
// get a reference to the recs world; as an example from the `setup` function of MUD.
const world = [...]
defineSystem(world, [Has(PlayerComponent), Has(Name), HasValue(PositionComponent, {x: 0, y: 0}], ({entity, component, value, type}) => {
// every time an entity enter, exit, or get updated within a query; this callback with fire
// the "type" will match these: UpdateType.Enter, UpdateType.Exit, UpdateType.Update
// as an example, if a new entity is named, has a player component, and is at the center of the universe; the callback fires
// if the name of a player at the center of the universe changes, the callback will also fire for that entity
// let's only log when a new entity matches this query
// every time a named player reaches {0, 0}; we want to log their name
if(type !== UpdateType.Enter) return
console.log(getComponentValueStrict(NameComponent, entity) + " reached the center!")
})
```
### Reacting to queries with React and `@latticexyz/react`
```tsx
import { useEntityQuery } from "@latticexyz/react";
import { Has, HasValue, getComponentValueStrict } from "@latticexyz/recs";
function ExampleComponent() {
const { components, world } = useMUD;
const { NameComponent, PlayerComponent, PositionCompoennt } = components
// get a list of all entities that are named, players, and at the center of the universe
// it is reactive and will trigger a re-render when the set of matching queries update
const entities = useEntityQuery([Has(PlayerComponent), Has(Name), HasValue(PositionComponent, {x: 0, y: 0}])
// [entity1, entity2, ...]
return(
<div>
<span>Players at the center:</span>
<ul>
{entities.map(entity => (
<li>{getComponentValueStrict(NameComponent, entity)}</li>
))}
</ul>
</div>
)
}
```

0 comments on commit 82d62a8

Please sign in to comment.