Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ request to fix it.
- [lustre/element] `element.advanced` correctly respects the `self_closing` and `void` options when generating HTML strings.
- [lustre/event] Fixed a bug where debounced events inside components decoded using the wrong event data.
- [lustre/runtime] Fixed a bug where nested compoents would sometimes render one animation frame later.
- [lustre/runtime] Fixed a bug where providing the same context multiple times would re-render all subscribed components.

## [v5.3.5] - 2025-09-03

Expand Down
7 changes: 7 additions & 0 deletions src/lustre/runtime/client/runtime.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as Events from "../../vdom/events.mjs";
import { Reconciler } from "../../vdom/reconciler.ffi.mjs";
import { virtualise } from "../../vdom/virtualise.ffi.mjs";
import { document } from "../../internals/constants.ffi.mjs";
import { isEqual } from "../../internals/equals.ffi.mjs";

//

Expand Down Expand Up @@ -134,6 +135,12 @@ export class Runtime {
} else {
const context = this.#contexts.get(key);

// if the new context we provide is equal to the current context,
// we don't have to notify our subscribers about the change.
if (isEqual(context.value, value)) {
return;
}

context.value = value;

for (let i = context.subscribers.length - 1; i >= 0; i--) {
Expand Down
2 changes: 2 additions & 0 deletions src/lustre/runtime/client/server_component.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ export class ServerComponent extends HTMLElement {
} else {
const context = this.#contexts.get(key);

// we don't have to compare here, since the server runtime only provides us values
// if they are already different.
context.value = value;

for (let i = context.subscribers.length - 1; i >= 0; i--) {
Expand Down
7 changes: 7 additions & 0 deletions src/lustre/runtime/server/runtime.ffi.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as Diff from "../../vdom/diff.mjs";
import * as Events from "../../vdom/events.mjs";
import { isEqual } from "../../internals/equals.ffi.mjs";
import {
ClientDispatchedMessage,
ClientRegisteredCallback,
Expand Down Expand Up @@ -118,6 +119,12 @@ export class Runtime {

case EffectProvidedValue: {
const { key, value } = msg;
const existing = Dict.get(this.#providers, key);
// we do not need to broadcast an update if the provided value is the same.
if (existing.isOk() && isEqual(existing[0], value)) {
return undefined;
}

this.#providers = Dict.insert(this.#providers, key, value);
this.broadcast(Transport.provide(key, value));

Expand Down
21 changes: 14 additions & 7 deletions src/lustre/runtime/server/runtime.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,20 @@ fn loop(
}

EffectProvidedValue(key:, value:) -> {
let providers = dict.insert(state.providers, key, value)

broadcast(
state.subscribers,
state.callbacks,
transport.provide(key, value),
)
let providers = case dict.get(state.providers, key) {
// we do not need to broadcast an update if the provided value is the same.
Ok(old_value) if old_value == value -> state.providers

_ -> {
broadcast(
state.subscribers,
state.callbacks,
transport.provide(key, value),
)

dict.insert(state.providers, key, value)
}
}

actor.continue(State(..state, providers:))
}
Expand Down