threlte logo
@threlte/core

Utilities

Threlte comes with a few utilities that are catered towards the use in a Threlte application but can also be used in other projects.

watch

Watch a single store or multiple stores and call a callback when they change to trigger side effects. The callback will be called immediately with the current value of the store.

const store = writable(0)
watch(store, (value) => {
  console.log(value) // 0
})
This utility function needs to be called during component initialization.

You can also watch multiple stores:

const store1 = writable(0)
const store2 = writable(1)
watch([store1, store2], ([value1, value2]) => {
  console.log(value1, value2) // 0 1
})

The callback can return a cleanup function that will be called when the stores change again.

const store = writable(0)
watch(store, (value) => {
  console.log(value) // 0
  return () => {
    console.log('cleanup')
  }
})

memoize

Use a single store or multiple stores and return the value(s) as an object. This is useful for using stores in a non-reactive way e.g. in loops.

const numberStore = writable(0)
const memoized = memoize(numberStore) // { current: 0 }

useTask(() => {
  numberStore.update((n) => n + 1)
  console.log(memoized.current) // 1, 2, 3, ...
})

const stringStore = writable('hello')
const memoized = memoize([numberStore, stringStore])
console.log(memoized.current) // [0, 'hello']
This utility function needs to be called during component initialization.

You can also pass a transform function to transform the values:

const store = writable(0)
const doubled = memoize(store, (n) => n * 2) // { current: 0 }

useTask(() => {
  store.update((n) => n + 1)
  console.log(doubled.current) // 2, 4, 6, ...
})

asyncWritable

Creates a writable store that is initialized with a promise. The store also directly implements the then and catch methods of the promise so that it can be used in await expressions and {#await} blocks of Svelte.

<script>
  import { asyncWritable } from '@threlte/core'

  const asyncOperation = async () => {
    // Do something async
  }
  const store = asyncWritable(asyncOperation())
  $: console.log($store) // asyncOperation result
</script>

<h1>
  {#await store then data}
    // Do something with the data
  {/await}
</h1>

This type of store is the return type of the load function of useLoader.

If an error occurs in the promise, the error will be logged to the console and the error can be accessed via the error property of the store which is also a store.

<script>
  import { asyncWritable } from '@threlte/core'

  const asyncOp = async () => {
    throw new Error('Something went wrong')
  }

  const store = asyncWritable(asyncOp())
  const error = store.error

  $: console.log($store) // undefined
  $: console.log($error) // Error: Something went wrong
</script>

currentWritable

A writable store that also has a current property that is updated synchronously. For use in non-reactive contexts e.g. loops where unwrapping a store every frame (with Svelte’s get method) is expensive.

const store = currentWritable(0)

useTask(() => {
  console.log(store.current) // 0
})

forwardEventHandlers

Natively, Svelte has no way of passing down event handlers inside a component to a child component. Events have to be hand-wired upstream. This function allows you to forward event handlers from a parent component to a child component as if they were declared on the child component itself.

Child.svelte
<script>
  import { forwardEventHandlers } from '@threlte/core'
  const dispatchingComponent = forwardEventHandlers()
</script>

<OtherChildComponent bind:this={$dispatchingComponent} />

Now, when implementing <Child> and adding event handlers via on:eventname, those event handlers will be forwarded to <OtherChildComponent>:

Parent.svelte
<script>
  import Child from './Child.svelte'
</script>

<Child on:click={() => console.log('clicked')} />

If OtherChildComponent.svelte now dispatches a click event, the event handler in Parent.svelte will be called.


createRawEventDispatcher

This event dispatcher creates raw events unlike Svelte’s own event dispatcher which nests the data in a detail object (CustomEvent<Payload>). This is not nesessary for a lot of Threlte use cases and makes it harder to work with the payload.

ComponentA.svelte
<script>
  const dispatch = createRawEventDispatcher<{ foo: string }>()
  dispatch('foo', 'bar')
</script>
ComponentB.svelte
<ComponentA on:foo={(e) => console.log(e)} />
<!-- 'bar' -->