@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
})
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 }
useFrame(() => {
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']
You can also pass a transform function to transform the values:
const store = writable(0)
const doubled = memoize(store, (n) => n * 2) // { current: 0 }
useFrame(() => {
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)
useFrame(() => {
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.
<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>
:
<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.
<script>
const dispatch = createRawEventDispatcher<{ foo: string }>()
dispatch('foo', 'bar')
</script>
<ComponentA on:foo={(e) => console.log(e)} />
<!-- 'bar' -->