In lustre, I understand that a msg hits the update function which then updates the model if necessary and then the view function is recalled with the updated state.... I want a particular message to update state and then after the view is rendered I want a secondary message triggered which will update the view again, how could I achieve this, would this be with an effect?
#lustre, message chaining?
1 messages · Page 1 of 1 (latest)
What is your use case exactly? Why do you need to launch another message after the first one? 🙂 Is it for animation purpose?
Yes, I want to add some classes then wait 1 frame and switch the classes
When the state of a menu item is toggled I will set entering to true, remove display:none, and add the necessary opacity-0 transition duration etc... then one frame later remove opacity-0 and add opacity-100
Yup, that was my intuition. I’d build my own Effect using effect.from, and dispatching the message you’re interested in. I’d wrap the dispatching in a setTimeout though, to make sure 100ms pass before repainting. 🙂
I don't think that is necessary, I'm considering copying the way alpine js does it it does it on th3 very next frame
I want a particular message to update state and then after the view is rendered I want a secondary message triggered which will update the view again
dispatch a message after an animation frame
Then as Hayleigh point it, you’re effectively looking for requestAnimationFrame 😉
I'll try when I get home
https://gist.github.com/hayleigh-dot-dev/07b482d40bc93527c015105b57223b43 maybe i will port this to gleam
I still need a little help on this unfortunately. Do i need that plinth package, or is there any example on how i would hook into that requestAnimationFrame() using lustre, it's on the window right, not on the element.
ok, so i can ffi into javascript to call window.requestAnimationFrame() but when do i call that, in my update function when my transition should start... then the thing i want to do in the request animation frame is to fire a message with effect.from?
was thinking something like this:
pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
case msg {
UserClickedProducts -> {
window.request_animation_frame(start_transition)
#(Model(..model, is_entering: True), effect.none())
}
TransitionStarted -> #(Model(..model, entered: True), effect.none())
}
}
fn start_transition(_timestamp: Float) {
effect.from(fn(dispatch) {
TransitionStarted
|> dispatch
})
Nil
}
Where would I return the effect from
What is the return type of your update function ^.^
#(Model, Effect(Msg))
Don't I want the effect to happen in the requestAnimationFrame callback
A couple times... I'll go read it again, sorry if I'm coming off really dumb here, it's completely new at of doing things, and something is not clicking for me
No all good just good to know what youve read or not
So in lustre, side effects (eg things that affect the outside world) dont happen in your lustre program
instead, we essentially construct a description of a side effect for the runtime to perform, and then return those side effects from our update function
pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
case msg {
UserClickedProducts -> #(Model(..model, is_entering: True), start_transition())
TransitionStarted -> #(Model(..model, entered: True), effect.none())
}
}
fn start_transition() {
use dispatch <- effect.from
use _timestamp <- window.request_animation_frame
dispatch(TransitionStarted
}
you would write that like this
"construct an effect that after one animation frame dispatches a TransitionStarted message back to our program"
I was gonna say ah OK I got it, but that vastly overestimates my understanding on the topic. I'll try this approach... I think I just need to get some repitition in
Its quite a different mental model
@celest topaz I might look for some of richard feldman's talks on elm. Lustre has some differences from Elm but they follow a similar architecture.
It helped me clarify some of the concepts around model-view-update
So I added some print statements to see when stuff happens, i click the button, and the first message gets set i see "User clicked products" in the console, but the print statements in start_transition never fire:
pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
case msg {
UserClickedProducts -> {
io.println("User clicked products")
#(Model(..model, is_entering: True), start_transition())
}
TransitionStarted -> {
io.println("Transition started")
#(Model(..model, entered: True), effect.none())
}
}
}
fn start_transition() -> Effect(Msg) {
io.println("start_transition()")
use dispatch <- effect.from
use _ts <- window.request_animation_frame
io.println("Request animation_frame")
dispatch(TransitionStarted)
}
I got this working now, I'm just working on refactoring to make a better api for adding transitions on everything
This is what I've landed on so far for typing of transitions and a helper function to get the classes:
pub type Transition {
Transition(
direction: TransitionDirection,
complete: Bool,
transitioning: Bool,
enter_classes: TransitionClasses,
leave_classes: TransitionClasses,
)
}
pub type TransitionDirection {
Entering
Leaving
}
pub type TransitionClasses {
TransitionClasses(all: String, from: String, to: String)
}
pub fn get_classes(transition: Transition) {
case transition {
Transition(direction: Entering, ..) ->
case transition.complete {
False ->
transition.enter_classes.all <> " " <> transition.enter_classes.from
True ->
transition.enter_classes.all <> " " <> transition.enter_classes.to
}
Transition(direction: Leaving, ..) ->
case transition.complete {
False ->
transition.leave_classes.all <> " " <> transition.leave_classes.from
True ->
transition.leave_classes.all <> " " <> transition.leave_classes.to
}
}
}
Then i apply the classes to my component like this:
pub fn product_flyout(transition: types.Transition) {
let classes = types.get_classes(transition)
div(
[
class(classes),
class(
"absolute -left-8 top-full z-10 mt-3 w-screen max-w-md overflow-hidden rounded-3xl bg-white shadow-lg ring-1 ring-gray-900/5",
),
],
[div([class("p-4")], [analytics_menu_item()]), flyout_footer()],
)
}