-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Describe the problem
I stumbled upon this problem on a table component that receives an array of items and an array of available filters as props.
The component manages its own state, meaning actived filters and the list of filtered items were internal component variables.
Now I need to check if all the filtered items meet some criteria from the parent element. To do this, I would make the filtered_items variable bindable. Currently, the filtered_items variable was declaras like this:
<script lang="ts">
const {items = $bindable(), filters = []} = $props();
let applied_filters = $state({});//Logic to manage this variable has been omitted
const filter = ()=>{
const active_filters = Object.values(applied_filters).filter(Boolean);
const filtered = items.filter(x => active_filters.every((clause) => clause.evaluate(x)));
return filtered;
}
const filtered_items = $bindable($derived.by(filter));
</script>If I wanted to make filtered_items bindable to be able to know which items are currently displayed on the table from the parent component, I would need to use an $effect, rather than a $derived:
<script lang="ts">
const {items = $bindable(), filters = [], filtered_items = $bindable()} = $props();
let applied_filters = $state({});//Logic to manage this variable has been omitted
$effect(()=>{
const active_filters = Object.values(applied_filters).filter(Boolean);
const filtered = items.filter(x => active_filters.every((clause) => clause.evaluate(x)));
filtered_items = filtered;
});
</script>This is philosophically wrong. filtered_items is still a $derived but we are using an $effect to do that just because the compiler will complain about something like this:
<script lang="ts">
const {items = $bindable(), filters = [], filtered_items = $bindable($derived.by(filter))} = $props();
let applied_filters = $state({});//Logic to manage this variable has been omitted
const filter = ()=>{
const active_filters = Object.values(applied_filters).filter(Boolean);
const filtered = items.filter(x => active_filters.every((clause) => clause.evaluate(x)));
return filtered;
}
</script>Describe the proposed solution
I would like to be able to use $derived and $derived.by in props destructuring. Now that deriveds can be writable I don't see why this shouldn't be allowed.
Having to change from a $derived to an $effect just because you want the variable to be readable from outside seems wrong.
We may want to differentiate between writable deriveds and readonly ones. For this we can use $bindable($derived()) (writable) VS just $derived() (readonly, can still be bound, any initial value provided by the parent component will be ignored).
Importance
nice to have