Skip to content

Commit

Permalink
feat: allow usage of getContext() within $derived runes (#13830)
Browse files Browse the repository at this point in the history
Closes #13493.

This PR allows the usage of getContext() inside $derived runes. Previously, you could use it, but only on init and not updates – and this inconsistency was unnecessary. We can make it work just like we do in other places.
  • Loading branch information
trueadm authored Oct 23, 2024
1 parent 96e2d5a commit d621f59
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-llamas-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

feat: allow usage of getContext() within $derived runes
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dom/elements/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ export function apply(

if (typeof handler === 'function') {
handler.apply(element, args);
} else if (has_side_effects || handler != null) {
} else if (has_side_effects || handler != null || error) {
const filename = component?.[FILENAME];
const location = loc ? ` at ${filename}:${loc[0]}:${loc[1]}` : ` in ${filename}`;

Expand Down
6 changes: 4 additions & 2 deletions packages/svelte/src/internal/client/reactivity/deriveds.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
skip_reaction,
update_reaction,
increment_version,
set_active_effect
set_active_effect,
component_context
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import * as e from '../errors.js';
Expand All @@ -44,6 +45,7 @@ export function derived(fn) {
/** @type {Derived<V>} */
const signal = {
children: null,
ctx: component_context,
deps: null,
equals,
f: flags,
Expand Down Expand Up @@ -169,5 +171,5 @@ export function destroy_derived(signal) {
set_signal_status(signal, DESTROYED);

// TODO we need to ensure we remove the derived from any parent derives
signal.v = signal.children = signal.deps = signal.reactions = null;
signal.v = signal.children = signal.deps = signal.ctx = signal.reactions = null;
}
4 changes: 2 additions & 2 deletions packages/svelte/src/internal/client/reactivity/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface Value<V = unknown> extends Signal {
}

export interface Reaction extends Signal {
/** The associated component context */
ctx: null | ComponentContext;
/** The reaction function */
fn: null | Function;
/** Signals that this signal reads from */
Expand All @@ -40,8 +42,6 @@ export interface Effect extends Reaction {
*/
nodes_start: null | TemplateNode;
nodes_end: null | TemplateNode;
/** The associated component context */
ctx: null | ComponentContext;
/** Reactions created inside this signal */
deriveds: null | Derived[];
/** The effect function */
Expand Down
5 changes: 3 additions & 2 deletions packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ export function update_reaction(reaction) {
var previous_reaction = active_reaction;
var previous_skip_reaction = skip_reaction;
var prev_derived_sources = derived_sources;
var previous_component_context = component_context;
var flags = reaction.f;

new_deps = /** @type {null | Value[]} */ (null);
Expand All @@ -310,6 +311,7 @@ export function update_reaction(reaction) {
active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0;
derived_sources = null;
component_context = reaction.ctx;

try {
var result = /** @type {Function} */ (0, reaction.fn)();
Expand Down Expand Up @@ -347,6 +349,7 @@ export function update_reaction(reaction) {
active_reaction = previous_reaction;
skip_reaction = previous_skip_reaction;
derived_sources = prev_derived_sources;
component_context = previous_component_context;
}
}

Expand Down Expand Up @@ -422,7 +425,6 @@ export function update_effect(effect) {
var previous_component_context = component_context;

active_effect = effect;
component_context = effect.ctx;

if (DEV) {
var previous_component_fn = dev_current_component_function;
Expand All @@ -449,7 +451,6 @@ export function update_effect(effect) {
handle_error(/** @type {Error} */ (error), effect, previous_component_context);
} finally {
active_effect = previous_effect;
component_context = previous_component_context;

if (DEV) {
dev_current_component_function = previous_component_fn;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
import { getContext } from 'svelte'
let count = $state(0);
let total = $derived(multiply(count));
function multiply(num) {
const context = getContext("key");
return num * context;
}
</script>

<button onclick={() => count++}>{total}</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { flushSync } from 'svelte';
import { test } from '../../test';

export default test({
html: `<button>0</button>`,

test({ assert, target }) {
const btn = target.querySelector('button');

flushSync(() => {
btn?.click();
});

assert.htmlEqual(target.innerHTML, `<button>10</button>`);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
import { setContext } from 'svelte'
import Child from './Child.svelte'
setContext("key", 10)
</script>

<Child />

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ export default test({
},

test({ assert, target, warnings }) {
/** @type {any} */
let error;

const handler = (/** @type {any}} */ e) => {
error = e.error;
e.stopImmediatePropagation();
};

window.addEventListener('error', handler, true);

target.querySelector('button')?.click();

assert.throws(() => {
throw error;
}, /state_unsafe_mutation/);

window.removeEventListener('error', handler, true);

assert.deepEqual(warnings, [
'`click` handler at Button.svelte:5:9 should be a function. Did you mean to add a leading `() =>`?'
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ export default test({
},

test({ assert, target, warnings }) {
/** @type {any} */
let error;

const handler = (/** @type {any} */ e) => {
error = e.error;
e.stopImmediatePropagation();
};

window.addEventListener('error', handler, true);

target.querySelector('button')?.click();

assert.throws(() => {
throw error;
}, /state_unsafe_mutation/);

window.removeEventListener('error', handler, true);

assert.deepEqual(warnings, [
'`click` handler at main.svelte:9:17 should be a function. Did you mean to remove the trailing `()`?'
]);
Expand Down

0 comments on commit d621f59

Please sign in to comment.