Skip to content

Async Data

@Resource binds an async resource directly to a component field. It tracks loading, error, and data state reactively.

Basic usage

tsx
import { Component, State, Resource } from '@praxisjs/decorators'
import { StatefulComponent } from '@praxisjs/core'

@Component()
class UserProfile extends StatefulComponent {
  @State() userId = 1

  @Resource(() => fetch(`/api/users/${this.userId}`).then(r => r.json()))
  user!: ResourceInstance<User>

  render() {
    return (
      <div>
        {() => this.user.pending() && <p>Loading...</p>}
        {() => this.user.error() && <p>Error: {this.user.error()?.message}</p>}
        {() => this.user.data() && <p>Hello, {this.user.data()!.name}</p>}
      </div>
    )
  }
}

The resource automatically re-runs when this.userId changes, because the fetcher reads a signal.


Resource state

PropertyTypeDescription
.data()T | nullThe resolved value
.pending()booleantrue while the fetch is in-flight
.error()unknownThe last error, or null
.status()'idle' | 'pending' | 'success' | 'error'Full status

Options

tsx
@Resource(() => api.getUsers(), {
  initialData: [],       // value of .data() before first fetch
  immediate: false,      // don't fetch automatically (default: true)
  keepPreviousData: true // keep old data while refetching
})
users!: ResourceInstance<User[]>

Refetch, cancel, and mutate

tsx
// Manually trigger a new fetch
<button onClick={() => this.user.refetch()}>Refresh</button>

// Cancel an in-flight request
<button onClick={() => this.user.cancel()}>Cancel</button>

Optimistic updates with mutate():

tsx
async save(newName: string) {
  // Update UI immediately
  this.user.mutate({ ...this.user.data()!, name: newName })

  // Then persist and confirm
  await api.updateUser({ name: newName })
  this.user.refetch()
}

Reactive dependencies

Any signal read inside the fetcher is a dependency — when it changes, the resource cancels any pending request and starts a new one:

tsx
@State() page = 1
@State() search = ''

@Resource(() =>
  fetch(`/api/search?q=${this.search}&page=${this.page}`).then(r => r.json())
)
results!: ResourceInstance<SearchResult[]>

Released under the MIT License.