Skip to content

Allow $derived to optionally receive callbacks #9984

@Thiagolino8

Description

@Thiagolino8

Describe the problem

Currently $derived only receives values, which maintains a cleaner syntax when using expressions
But when it is necessary to use any minimally more complex logic, it is necessary to get out of your mental model and write much less natural code, extracting the logic into a separate named function (naming is difficult), or creating an IIFE

// This is nice
const double = $derived(count * 2)

// This is abominable
function calcTotal() {
  const discountAmount = cartTotal * promoDiscount;
  const taxedAmount = (cartTotal - discountAmount) * taxRate;

  return cartTotal - discountAmount + taxedAmount;
}

const total = $derived(calcTotal())

// I wouldn't pass this on a PR
const total = $derived((() => {
  const discountAmount = cartTotal * promoDiscount;
  const taxedAmount = (cartTotal - discountAmount) * taxRate;

  return cartTotal - discountAmount + taxedAmount;
})());

Describe the proposed solution

Unlike #9250 and #9968 that suggest changes to the api or a new method, I suggest an addition, supporting callbacks using function overloading
This way it would be possible to keep the simplest syntax for expressions and use callbacks only when necessary
Something like:

function<T>$derived(value: T)
function<T>$derived(value: () => T)
function<T>$derived(value: T | () => T) {
  if (value instanceOf Function) return value();
  return value
}

Not only would this facilitate the use of more complex logic, it would also make it possible to receive reactive values from functions that return callbacks

// utils.svelte.js
export const previousState = (value) => {
  let cur
  let prev = $state()

  $effect.pre(() => {
    prev = cur
    cur = value()
  });

  return () => prev
}

// App.svelte
<script>
  import {previousState} from "./utils.svelte.js"
	
  let value = $state('');

  const prev = $derived(previousState(() => value))
</script>

<input bind:value />
<p>Previous State: {prev}</p>

Alternatives considered

Continue writing code unnaturally by extracting into named functions or IIFEs

Importance

nice to have

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions