These are chat archives for mithriljs/mithril.js

22nd
Jul 2019
spacejack
@spacejack
Jul 22 03:03
dammit @JAForbes you convinced me to try rewriting a more database-style ECS now
I think you could do some data locality optimizations this way
some
There's a certain amount of small object churn no matter what
But if you used indices instead of objects in your components DB, you could put a lot of data in a typed array and reference it by index.
const db = {
    position: new Map<number, number>(),
    velocity: new Map<number, number>(),
}
spacejack
@spacejack
Jul 22 03:10
const positions = new Float64Array(MAX_POSITIONS * DIMENSIONS)
If the data parts of the database are in typed arrays then you might be able to write some systems as WASM modules
Gilbert
@gilbert
Jul 22 03:31
import { o, h } from 'sinuous';

const counter = o(0);
const view = () => {
  return html`
    <div>Counter ${counter}</div>
  `;
};

document.body.append(view());
setInterval(() => counter(counter() + 1), 1000);
interesting idea from a library called sinuous
seems like passing an observable to the view auto-subscribes it
spacejack
@spacejack
Jul 22 03:58
oh that's funny
I'm not sure how it would look with something more complicated
It's only 2K so I guess it's not trying to be a full solution for apps
James Forbes
@JAForbes
Jul 22 05:24
@spacejack that's an amazing idea!
is it ok to have holes in the array?
I'm playing with using meiosis + services as an ECS right now in my spare time
spacejack
@spacejack
Jul 22 05:25
I guess you could design the data layout however you think works best
James Forbes
@JAForbes
Jul 22 05:26
yeah
spacejack
@spacejack
Jul 22 05:26
typed arrays can't have holes, but you could just allocate some large block and manage it yourself
James Forbes
@JAForbes
Jul 22 05:26
deletion is the most awkward part of ECS anyway so it'd be fine to just mark it as "deleted" and record where the gaps are so when new items are allocated it just reuses the same object
spacejack
@spacejack
Jul 22 05:26
Using a shared array with a wasm module would be cool
James Forbes
@JAForbes
Jul 22 05:26
like object pooling I guess
yeah it would
@gilbert that's really cool too!
svelte like, but runtime
that'd be really simple to rig up to with template literals come to think
super smart
Patrik Johnson
@orbitbot
Jul 22 09:03
the codebase was somewhat over-organised, couldn't bother to get a good idea of what it was actually doing
Barney Carroll
@barneycarroll
Jul 22 09:34
Just catching up and riffing on these API musings... This year's hot new flavour now Hooks have permeated the planet is avoiding methods. Been playing with my [anyComponentMethods](vnodeNow, vnodeBefore) branch and... Yeah the feel of the semantics of simply using view for all concerns can be weird. In practice, implementing this API makes all of oninit, oncreate, onupdate, onbeforeupdate redundant:
export default {
  view: (current, previous) => {
    if(!previous){
      // oninit

      Promise.resolve().then(() => {
        // oncreate
      })
    }
    else {
      // onbeforeupdate

      Promise.resolve().then(() => {
        // onupdate
      })

      return previous // onbeforeupdate : () => false
    }

    return x // view : () => x
  }
}
James Forbes
@JAForbes
Jul 22 09:37
I really like having separate affordances for create/update etc, one of my fav parts of v1
config's isInit was pretty confusing
Patrik Johnson
@orbitbot
Jul 22 09:40
the main issue I have with :point_up: is that you're essentially forced to recreate the lifecycle methods yourself every time that you need them
even if you only use a few at a time, at least my first take on this is that you're just making things harder to read at a glance
James Forbes
@JAForbes
Jul 22 09:41
and it's not super obvious that they are being recreated
oncreate.map( ... ) though, that's the 🔥 :D
Barney Carroll
@barneycarroll
Jul 22 09:41
Right
Patrik Johnson
@orbitbot
Jul 22 09:41
🤷
Barney Carroll
@barneycarroll
Jul 22 09:41
The problem with the above is it's a straw man, nobody would actually do this
James Forbes
@JAForbes
Jul 22 09:42
😎
Patrik Johnson
@orbitbot
Jul 22 09:42
that's basically just doing the same with an event-emitter thingie anyways
James Forbes
@JAForbes
Jul 22 09:42
It's really clever though @barneycarroll
Barney Carroll
@barneycarroll
Jul 22 09:42
Just a proof of concept to show you don't need hooks
BUT
What it does end up with is not having the cognitive dissonance of config vs oncreate + onupdate
James Forbes
@JAForbes
Jul 22 09:42
I think config made more sense when you didn't have closures
maybe...
Patrik Johnson
@orbitbot
Jul 22 09:42
(can't remember config usage in detail)
Barney Carroll
@barneycarroll
Jul 22 09:42
The whole "which scope should this logic go to" disappears
James Forbes
@JAForbes
Jul 22 09:43
because you might want to have one shared context for event listeners, in order to remove them, and this is eww
Barney Carroll
@barneycarroll
Jul 22 09:43
Effects you want to run after every render
James Forbes
@JAForbes
Jul 22 09:43
and vnode.state is the worst ( ducks )
Barney Carroll
@barneycarroll
Jul 22 09:43
:point_up: right!
Patrik Johnson
@orbitbot
Jul 22 09:43
I kinda get the feeling that vnode.stateis getting the "we're not talking about this any more, let's forget it ever was there"-treatment
James Forbes
@JAForbes
Jul 22 09:44
yeah, rightfully
Barney Carroll
@barneycarroll
Jul 22 09:44
So I was toying with the idea of having an alwaysmethod which fudged the (current, previous) thing to also cater for onbeforeremove + onremove
Patrik Johnson
@orbitbot
Jul 22 09:44
Isiah had something similar to Barney's example going on in his v3 musings repo, but from what I gathered the main driver there was more of a perf boost or library implementation simplicity thing
Barney Carroll
@barneycarroll
Jul 22 09:44
So you really only need one method and no extra API surface at all
James Forbes
@JAForbes
Jul 22 09:44
react has something like, getComputedState or something
I really like the idea of all the hooks being reducers, that's solid @barneycarroll

or we have no methods just streams, and we return a function which is the view.

Ok I'm done 😀

Patrik Johnson
@orbitbot
Jul 22 09:45
what would always look like, outside of a bad Bon Jovi music video?
James Forbes
@JAForbes
Jul 22 09:45

streams are reducers already, because of scan, it's all built in

ok now I'm done

Barney Carroll
@barneycarroll
Jul 22 09:46
I was kinda thinking, at this point, why not just get rid of almost all the verbiage?
export default (initial) => {
  // init scope

  return (current, previous) => {
    // all other concerns
  }
}
James Forbes
@JAForbes
Jul 22 09:46
oooo
oooo
ok I want to try that
Barney Carroll
@barneycarroll
Jul 22 09:46
Look mum, no words
Patrik Johnson
@orbitbot
Jul 22 09:47
so the initial closure is just for whatever you need, and the returned function gets called immediately?
James Forbes
@JAForbes
Jul 22 09:47
the returned function is the view, but because it has the current and previous you can infer all other states
actually, how do you do onbeforeremove barney?
Barney Carroll
@barneycarroll
Jul 22 09:47
Sure, we can introduce convenience APIs to avoid people reaching for await Promise.resolve() or whatever, but in practice it's not necessary
Ah that's the dirty part :P
James Forbes
@JAForbes
Jul 22 09:48
yeah...
Barney Carroll
@barneycarroll
Jul 22 09:48
The returned function also runs onbeforeremove, and can be infered from the first argument being nullish :grimacing:
James Forbes
@JAForbes
Jul 22 09:48
nah
that ain't it 😀
how do you differentiate onremove and onbeforeremove then?
is onremove, both null
Barney Carroll
@barneycarroll
Jul 22 09:49
You never really needed to
James Forbes
@JAForbes
Jul 22 09:49
best answer
because onbeforeremove returns a promise you can do your cleanup in the promise chain?
Barney Carroll
@barneycarroll
Jul 22 09:50
Yeah
James Forbes
@JAForbes
Jul 22 09:50
can you though?
Barney Carroll
@barneycarroll
Jul 22 09:50
Why not?
James Forbes
@JAForbes
Jul 22 09:50
yeah thinking about it, I guess you are right
Barney Carroll
@barneycarroll
Jul 22 09:51
I mean onbeforeremove is kinda nasty in that you break into asynchronous view reconciliation depending on whether it returns a thennable :grimacing:
James Forbes
@JAForbes
Jul 22 09:51
kind of annoying to not be able to fire and forget: onremove: f but yeah you're right
why can't it always be a promise?
I might have missed that
Barney Carroll
@barneycarroll
Jul 22 09:51
So there was this kinda desirability to keep onbeforeremove and onremove discrete in that you could establish a convention that onbeforeremove would only be used if you needed to delay removal
Well you introduce mandatory async view reconciliation which is... Kinda ugly?
James Forbes
@JAForbes
Jul 22 09:52
is it?
is it perceptible to the human eye?
Barney Carroll
@barneycarroll
Jul 22 09:53
Erm
I mean, it hurts my head
:P
James Forbes
@JAForbes
Jul 22 09:53
but you're mr zalgo right?
Barney Carroll
@barneycarroll
Jul 22 09:54
HEY
I'm just riffing on pre-existing constraints, man
James Forbes
@JAForbes
Jul 22 09:54
haha ok
Barney Carroll
@barneycarroll
Jul 22 09:54
No it is bad
Because of the tree
We've touched upon this in previous weird conversations that only make sense when you get API semantics tunnel vision
James Forbes
@JAForbes
Jul 22 09:55
right
Barney Carroll
@barneycarroll
Jul 22 09:56
Such as "should every onbeforeremove be called when the root node disappears?"
Patrik Johnson
@orbitbot
Jul 22 09:56

API semantics tunnel vision

what a concept :grinning:

Barney Carroll
@barneycarroll
Jul 22 09:57
The answer is no, you probably don't want "chat window collapse animation" and "sidebar slides out" to run when you're changing routes
(If you do, there are ways to make those requirements explicit)
Sorry @JAForbes,specifically the issue is that you build up these micro delays on every nested vnode before the higher order can resolve
James Forbes
@JAForbes
Jul 22 09:59
ahhh
Barney Carroll
@barneycarroll
Jul 22 09:59
Somebody who's good at computer science tell me if that's a practical issue or not, but in theory at least it just burns my brain
James Forbes
@JAForbes
Jul 22 10:00
assuming every layer had an onbeforeremove?
Barney Carroll
@barneycarroll
Jul 22 10:00
Yeah
James Forbes
@JAForbes
Jul 22 10:00
seems unlikely
Barney Carroll
@barneycarroll
Jul 22 10:00
Well
James Forbes
@JAForbes
Jul 22 10:00
you tend to animate out a parent element not it's children
a sidebar, you might fade it's children, and slide out the parent
Barney Carroll
@barneycarroll
Jul 22 10:00
Yeah yeah
James Forbes
@JAForbes
Jul 22 10:00
it's unlikely to be many right?
Patrik Johnson
@orbitbot
Jul 22 10:01
dunno if this is a CS question as is ... :slight_smile:
don't really know, strictly speaking you might have someone add an exit animation on every element they've rendered
Barney Carroll
@barneycarroll
Jul 22 10:02
There's potential for exponential algorithmic complexity on higher order node removal if this is fudged
Patrik Johnson
@orbitbot
Jul 22 10:02
which is a quite unlikely worst case, but it's not impossible
Barney Carroll
@barneycarroll
Jul 22 10:02
Just in knowing how to safely resolve tree removal
Patrik Johnson
@orbitbot
Jul 22 10:03
not sure if that statement on algorithmic complexity really holds true
Barney Carroll
@barneycarroll
Jul 22 10:03
Sorry, to be clear, there is a problem in doing what I'm suggesting - conflating onbeforeremove / onremove
onremove is guaranteed cleanup
James Forbes
@JAForbes
Jul 22 10:04
ahhh
Barney Carroll
@barneycarroll
Jul 22 10:04
onbeforeremove will only trigger if that node is removed, but its parent remains in place
James Forbes
@JAForbes
Jul 22 10:04
that seems sensible
Barney Carroll
@barneycarroll
Jul 22 10:05
In that scenario you need to interrupt normal diff resolution because the original node needs to remain until the promise is resolved
Patrik Johnson
@orbitbot
Jul 22 10:06
I agree that in practice it might turn into a complete clusterfsck, but it should be pretty deterministic
and with potential unresolved promises you can get into a deadlock
to me though, this whole discussion kind of emphasises the gut feeling I get that most of these API rewrite suggestions should actually be tested out if at all possible before they get too much stage time
James Forbes
@JAForbes
Jul 22 10:08
yeah I agree
Patrik Johnson
@orbitbot
Jul 22 10:08
the streams one should be simple to just tack on the existing v1/v2
James Forbes
@JAForbes
Jul 22 10:08
yeah
Barney Carroll
@barneycarroll
Jul 22 10:09
Well
It's worth observing at this point that our competitors don't feel onbeforeremove is worth implementing
Arguably it's the kind of thing that should be fulfilled with a higher-order component
James Forbes
@JAForbes
Jul 22 10:10
I've used onbeforeremove an order of magnitude more times than onremove
because when a node is removed, its listeners are too, and that's usually pretty reliable
Barney Carroll
@barneycarroll
Jul 22 10:10
Presumably because the timer thing is a straw man and you'd never actually build that kind of logic into a component-centric state
Component state cleanup is definitely a smell
Patrik Johnson
@orbitbot
Jul 22 10:13
well, with react where the discussion started, they pretty much have the convention of putting most stuff into component state no matter what
some years back there was stuff talking about "data only components" that were higher level and only passed objects to children that had views
Barney Carroll
@barneycarroll
Jul 22 10:14
context missing
Patrik Johnson
@orbitbot
Jul 22 10:14
and a lot of examples around still follow this pattern
James Forbes
@JAForbes
Jul 22 10:15
data only components make sense to me, render props are a great thing
Barney Carroll
@barneycarroll
Jul 22 10:15
Yeah they were awesome
In principle
Patrik Johnson
@orbitbot
Jul 22 10:15
I'm still of the opinion that you should not put that into a view tree if you can avoid it
Barney Carroll
@barneycarroll
Jul 22 10:16
Hmm
James Forbes
@JAForbes
Jul 22 10:16
I think most of the problems arise from everything being a component, but render props make a lot of sense for a lot of problems
Barney Carroll
@barneycarroll
Jul 22 10:16
I mean at a certain point, you need to bind your views to your view-related logic
Patrik Johnson
@orbitbot
Jul 22 10:16
so what I'm trying to say is that essentially a timer does make sense as something that you might need to show in a view, and since the apis are what they are you're bound to need some cleanup
Barney Carroll
@barneycarroll
Jul 22 10:16
Yeah it depends how you do it, 'concatenating concerns' can be glorious
See BSS components
James Forbes
@JAForbes
Jul 22 10:17
render props are great for the same reason .map is incredible. It completely saves the framework, the library, the component author from having to cater to every concern ahead of time
Barney Carroll
@barneycarroll
Jul 22 10:18
Mashing all my logic and style and structure into hyperscript can be beautiful. The fallacy with separation of concerns is that in practice it's often a case of pushing concatenation of concerns out of view for as long as possible, and ends up having the opposite to desired effect: by burrying the interdependencies, you make them harder to extricate and reason about later down the line

render props are great for the same reason .map is incredible. It completely saves the framework, the library, the component author from having to cater to every concern ahead of time

:100:

And unlike Higher Order Components and Composite Components, it's easy enough to follow / validate that practice
James Forbes
@JAForbes
Jul 22 10:20

yeah I think render props are great for WET, you can see if you keep doing the same thing in a render prop again and again and again and then decide to build it in to the component at a later time. At that point it's probably safe to add it to the API.

So adding a render prop interface by default when authoring a new component that manages state or effects in some way is almost always a good call I think.

yeah you can end up with a pyramid of doom, but I think that's more due to everything being a component, not render props
the nesting comes from the components not the render props
Barney Carroll
@barneycarroll
Jul 22 10:21
HOCs / CCs you're just relying on superficial mnemonics. It's a lot like Redux / Saga where you validate a 'feel' for best practices based on seeing enough switch / for of await statemenents
Patrik Johnson
@orbitbot
Jul 22 10:22
having just refreshed my mind on react's render props, the example they're using in their documentation is mouse pointer tracking which needs to be a component since you need the listeners in the UI. As for just passing data to something to display it, not particularly convinced it's that good of a pattern
Barney Carroll
@barneycarroll
Jul 22 10:22
Get your linter to guard against Bad Side Effects by enforcing a minimum number [Symbol.iterator] /LoC ratio
James Forbes
@JAForbes
Jul 22 10:22
Symbols were a mistake imo
Patrik Johnson
@orbitbot
Jul 22 10:22

It's a lot like Redux / Saga where you validate a 'feel' for best practices based on seeing enough switch / for of await statemenents

:joy:

James Forbes
@JAForbes
Jul 22 10:23
👏
Barney Carroll
@barneycarroll
Jul 22 10:23
:point_up: these are all places where there's view-based logic that's completely dissociated from structural requirements
Patrik Johnson
@orbitbot
Jul 22 10:25
I've seen the readme before, but really didn't spend much time on the repo otherwise IIRC
Barney Carroll
@barneycarroll
Jul 22 10:25
In practice I end up writing similar things off the cuff all the time. Inline & viewOf are genuinely useful, the rest are just examples
Patrik Johnson
@orbitbot
Jul 22 10:26
guess it may well be if you get in to the habit of it
Barney Carroll
@barneycarroll
Jul 22 10:27
This is one of those things which is really about virtual DOM semantics
As in, meaningful in terms of virtual DOM logic, but irrelevant to actual DOM
Like, m(Memo, whatever) tells the virtual DOM never to recompute the subtree
In practice that's equivalent to m.fragment({onbeforeupdate: () => false}, whatever). But semantics are built into source.
Waiter does that thing we were just talking about where you want to delay tree removal until subtree removal conditions have been fulfilled
Barney Carroll
@barneycarroll
Jul 22 10:32
In all of these cases, there's stateful logic involved which A) has no opinion on the shape or content of the eventual DOM B) is intrinsically and unavoidably linked to higher order virtual DOM concerns
Patrik Johnson
@orbitbot
Jul 22 10:41
:point_up: so that kind of thing I feel can be really useful
Barney Carroll
@barneycarroll
Jul 22 10:42
I'd be very appreciative of any ideas how to better express it
James Forbes
@JAForbes
Jul 22 10:42
I still think it's gold @barneycarroll
Barney Carroll
@barneycarroll
Jul 22 10:43
I was having this conversation with Dominic Ganaway again the other day
His insistence on 'virtual DOM' referring to the low level DOM reconciliation engine, my usage of it to refer to the higher order API idiom
Please signal boost my thinly veiled contempt for the hubris of people getting paid 6 figures to complain about the stupidity of the people their work is ostensibly meant to benefit https://twitter.com/_barneycarroll/status/1152868251464032256?s=20
Barney Carroll
@barneycarroll
Jul 22 10:48
I totally get what Dom's saying - he's doing very interesting work on redesigning React to be far less DOM-centric
But this points to the dreaded caste system of programming discourse
1) Library authors
2) Medium post authors
3) Consumers
0) TC-39
I think my failure to be able to explain the value proposition of Proponents is down to the unidirectional data flow of 1->3
Like it's impossible for 3 to communicate back with 1 with critical engagement, even asserting our own terminology is met with subconscious gatekeeping
Patrik Johnson
@orbitbot
Jul 22 11:46
I guess the problems with that end up being that people are actually facing quite different challenges in their day-to-day
Barney Carroll
@barneycarroll
Jul 22 11:46
Oh sure, these are all totally abstracted
Patrik Johnson
@orbitbot
Jul 22 11:47
I was at a guest lecture by Linus Torvalds at some point, and when he started going on about the actual things he was overseeing and working with I don't really believe there was anyone (out of a 3-400 person audience) that wasn't like "woosh"
Barney Carroll
@barneycarroll
Jul 22 11:48
Which is why Abramovs / James setTimeout thing is so interesting as a straw man, because it's so transparently useless in any kind of credible real world scenario, but somehow it really illustrates the mechanics better for all that
No risk of "but what if I want the button to be blue instead of red like the example"
Patrik Johnson
@orbitbot
Jul 22 11:49
one problem being that to 1) ( :point_up: ), 2) - 0) probably really don't know what they're talking about in most cases
Barney Carroll
@barneycarroll
Jul 22 11:49
Yeah
Patrik Johnson
@orbitbot
Jul 22 11:52

@barneycarroll btw, when you mentioned

I'd be very appreciative of any ideas how to better express it

wrt. proponents, what where you referring to?

naming of the individual helpers, or the readme text or something else?
Barney Carroll
@barneycarroll
Jul 22 12:04
So, I've talked about these a fair few times, gone through several iterations over the years...
But the idea of 'logic-only' / 'view-less' components still seems to a pretty dubious & arcane proposition whenever it comes up

In all of these cases, there's stateful logic involved which A) has no opinion on the shape or content of the eventual DOM B) is intrinsically and unavoidably linked to higher order virtual DOM concerns

Basically a way to express this that sticks

Patrik Johnson
@orbitbot
Jul 22 12:13
I wonder if the reusage of "components" becomes a bit of a misnomer
in that it's perhaps more of a "subtree manager" or "diff-rule-changer" or other helper
it's not really a "view-less" component as I'd think about it, more perhaps a "pass-through" helper (which are even in the docs) but instead of adding some static header or something you're actually changing the rules of whatever the children are
similar to the static subtree with a nested m.render that someone shared at some point (though IIRC that didn't consume child params)
ie. a roundabout way to try to say that it might be easier to communicate with slightly different terminology
Barney Carroll
@barneycarroll
Jul 22 12:17
Right, 'view-less' might be a misnomer. There is a view, it's all about view - but that view produces no DOM artefacts of its own
It's totally a component
Patrik Johnson
@orbitbot
Jul 22 12:18
strictly speaking sure, but might be easier to make it stick with a new label? :slight_smile:
Barney Carroll
@barneycarroll
Jul 22 12:18
The problem is in the conception that a virtual DOM component is intrinsically the same thing as a Ruby template or whatever
Patrik Johnson
@orbitbot
Jul 22 12:18
a HOC isn't particularly magic either as such, but it has a name
Barney Carroll
@barneycarroll
Jul 22 12:19
Yeah, I tried 'functor components', 'blind components'... Higher Order Components is a pretty grim name IMO.
a HOC isn't particularly magic either as such, but it has a name
I mean, if HOCs aren't magic then nothing in this world is really
"It's all Javascript"
Patrik Johnson
@orbitbot
Jul 22 12:20
what can I tell you :grinning:
...
Barney Carroll
@barneycarroll
Jul 22 12:20
Higher Order Components are extremely magical
Patrik Johnson
@orbitbot
Jul 22 12:20
I mean, they're talked about as magical things, but when you actually get down to it...
though at the same time extra terminology kind of muddies the waters more than anything else
Barney Carroll
@barneycarroll
Jul 22 12:21
And the name is part of it: people writing them forget why or how they do what they do, resulting in massive layers of complexity where the actual intent and mechanics are all but invisible.
Patrik Johnson
@orbitbot
Jul 22 12:21
so I guess we kind of end up on another tangent of intent as Scotty was mentioning the other day
Barney Carroll
@barneycarroll
Jul 22 12:21
Every time I've seen them in the wild I've managed to (eventually) pull them apart to reveal the defining pattern was entirely unnecessary
But most of the time even the authors couldn't say for sure whether I'd broken some untestable aspect of their behaviour or not, because they themselves couldn't fully rationalise their purpose
Patrik Johnson
@orbitbot
Jul 22 12:22
IME they're mostly useful as syntax sugar for library writers (in react at least)
Barney Carroll
@barneycarroll
Jul 22 12:22
This is extremely magical behaviour in practice, even if a certain class of clever programmer can eventually decipher it
Patrik Johnson
@orbitbot
Jul 22 12:25
hmm. yeah, I guess that part of the discussion starts to veer off into how much a programmer needs to understand of the frameworks / libraries they're using
KISS
on reflection, it feels a bit odd that I'm perfectly happy with stuff like m(Memo, ...) but don't feel that there should be data-only components (by which I'm assuming that there's a component that exposes data to its subtree only)
Barney Carroll
@barneycarroll
Jul 22 13:40
Mostly it's a case of instantiating and referencing data points as close to their only site of relevance as possible.
This is where BSS shines: rather than coming up with a shared reference (className), then writing its definition in one place and its invocation in another, simply write the expression in the one place where it's relevant, and loose the obnoxious context switching.
Barney Carroll
@barneycarroll
Jul 22 13:46
This breaks down a bit once the volume of complexity in the various distinguishable domains (structure, style, behaviour) gets too much: a 50 / 50 mix of BSS and hyperscript expression isn't a problem when the sum of the file fits on one screen. But if I've got 100 lines of BSS and 100 of hyperscript I might struggle to see the woods for the trees, so some kind of separation of concerns would be desirable.
The same applies with component logic. m(Inline) allows you to write a component as an expression rather than an external reference and an invocation. Now in practice any significant comlexity in a component is going to make that tough to read, so you'll eventually probably want to pull out the definition to an external module. But in the meantime, it's invaluable to collapse the API surface, and simply write and edit the references you need as they emerge, rather than opening MyNewComponent.js then switching back and forth between the call-site and the module definition to determine what moving parts you need.
Kevin Morotn
@TheSkyNet
Jul 22 13:57
I'm attempting to "subscribe to a map"
Barney Carroll
@barneycarroll
Jul 22 13:58
Could you edit that to put three backticks before and after that code?
Kevin Morotn
@TheSkyNet
Jul 22 13:59
yes hold on
i set the PostsService.comments.set(id, comments);
after the servise is run
Kevin Morotn
@TheSkyNet
Jul 22 14:05
i do a m.redraw() just to test but it's not calling the view function
Oscar
@osban
Jul 22 14:06
is that a promise call?
e.g. m.request
thats in the service
Oscar
@osban
Jul 22 14:09
you have to make sure your view can deal with the array being empty
Kevin Morotn
@TheSkyNet
Jul 22 14:09
m(Comments, {postId:post.id, oninit: initializeComments(post.id)})
initializeComments is just
PostsService.comments.set(id, []);
Oscar
@osban
Jul 22 14:10
so something like PostsService.comments.get(vnode.attrs.postId).length && PostsService.comments.get(vnode.attrs.postId).map(...)
Kevin Morotn
@TheSkyNet
Jul 22 14:10
I'm initializing with an empty array , so there should always bee an array.
Oscar
@osban
Jul 22 14:11
ah ok
and you're getting the comments back from the api, I'm assuming?
it would be helpful if you could simplify things and repro it in a flems
Kevin Morotn
@TheSkyNet
Jul 22 14:15
yep , will do
Kevin Morotn
@TheSkyNet
Jul 22 15:22
@osban link
sorry useful error Script error.
Oscar
@osban
Jul 22 15:43
@TheSkyNet try using m.mount instead of m.render :wink:
Kevin Morotn
@TheSkyNet
Jul 22 15:44
@osban yep
i'm bad thanks for your patients
Oscar
@osban
Jul 22 15:45
I moved the oninit to the Comment component itself, so you only have to pass the post.id
@TheSkyNet :+1:
Kevin Morotn
@TheSkyNet
Jul 22 16:15
@osban yes that seems to worki in the simple app , but im sitll haveing issues in my main app n
also thanks for all this help so far you are an awaosme human
ill try some things
Kevin Morotn
@TheSkyNet
Jul 22 16:24
m.redraw() will force the componets to redraw right?
Oscar
@osban
Jul 22 16:25
you're welcome :slight_smile: you can always share a flems if you need more help
yes, but redraw =/= re-init
(I have to run, bbl)
Scotty Simpson
@CreaturesInUnitards
Jul 22 17:05
@orbitbot @JAForbes @barneycarroll what if POJO components were considered "advanced usage: do not use until you know what you're doing"?
I think if we begin our thoughts about Mithril's component API with a single flavor — closure — we can simultaneously enhance and simplify the entire value proposition.
Much like how m.render is treated.
Scotty Simpson
@CreaturesInUnitards
Jul 22 17:15
And if we begin there, knowing that the vast bulk of any app's Mithril-specific code will be calls to m(, we then have only to explain the 2 possibilities as non-further-branching propositions: selector or component. And now the universe of possibilities are unleashed to the dev who just found the framework 16 minutes ago.
Scotty Simpson
@CreaturesInUnitards
Jul 22 17:20
And given that lifecycle is a core property of a component, we can then, without increasing the cognitive load, add "hooks" of the "I-have-lifecycle-dependent-properties-but-no-view" via our same, simple, elegant component API.
And what I love about @barneycarroll's ( now, then ) => { ... } thought experiment is its attempt to decrease surface. Of course he's the first to tell you it's not RW-friendly, since it asks us to roll our own lifecyle methods, but it's viable.
Scotty Simpson
@CreaturesInUnitards
Jul 22 17:35
In that light, we see that lIfecycle methods are really just sugar, but nothing about them necessitates having a view. So if we think of a component as ( view && lifecycle ) || view || lifecycle, and think of a view as model => view, we could agree that a component may:
  1. simply inform the model, or
  2. simply create the view based on the model, or
  3. both.
And now we have crazy power and we've learned a grand total of one code pattern.
Scotty Simpson
@CreaturesInUnitards
Jul 22 18:02

and vnode.state is the worst ( ducks )

It also has the benefit of removing the cognitive vortex of vnode.state from entry-level thinking. Also oninit.

Pim Brouwers
@pimbrouwers
Jul 22 19:22
hi everyone! new to the community and mithril (though I've been building client code for almost a decade). I was curious if anyone had experience using Mithril in a non-single page application?
Stephan Hoyer
@StephanHoyer
Jul 22 19:22
👋
Pim Brouwers
@pimbrouwers
Jul 22 19:24
And of course, where or not it was effective in that use case.
Stephan Hoyer
@StephanHoyer
Jul 22 19:24
In one client project I use it only for some ui elements, for the rest we use backbone (for historical reasons). Works great
Pim Brouwers
@pimbrouwers
Jul 22 19:26
How are you performing component activations? I've been toying with a declarative component registry to enable declarative rendering (https://jsfiddle.net/pimbrouwers/onezku4b/). Similar to what I'm used to in knockout, which shines in a traditional web app.
Stephan Hoyer
@StephanHoyer
Jul 22 19:26
I can't imagine a reason to not use it, other than haven extra 10 kb of JS
Pim Brouwers
@pimbrouwers
Jul 22 19:29
I feel similarly based on my limited exposure. Though, without a means of declaratively rendering the components I can't imagine I will use it.
Stephan Hoyer
@StephanHoyer
Jul 22 19:29
interesting pattern
Pim Brouwers
@pimbrouwers
Jul 22 19:30
the pattern is stolen from knockout, which is super useful in server-side apps
I suppose I answered my own question with that fiddle. However, I wanted to run it by some experts first, because it seems to really sell itself as a single-page application library.
Patrik Johnson
@orbitbot
Jul 22 19:32
what alternatives were you considering?
Stephan Hoyer
@StephanHoyer
Jul 22 19:32
I don't really know, if it's valid to create own tags without - in name and without registering tho.
Patrik Johnson
@orbitbot
Jul 22 19:33
I mean, usually there's a few ways to get to Rome, so to say
Pim Brouwers
@pimbrouwers
Jul 22 19:36
Preact, knockout or rivets likely.
Stephan Hoyer
@StephanHoyer
Jul 22 19:39
have you considered svelte?
if you only need a tool for ui-widgets, svelte seems a good fit
Pim Brouwers
@pimbrouwers
Jul 22 19:40
I'll check it out thank you!
Patrik Johnson
@orbitbot
Jul 22 20:03
hmm, rivets docs recommend using bower as a package manager...
ah, no wonder, no commits in 3 yrs
spacejack
@spacejack
Jul 22 20:30
@pimbrouwers we've built sites that are mostly traditional server-rendered pages with heavy use of Mithril components throughout.
We came up with a system where we look for empty container elements and mount corresponding components.
The server could supply initial data to the component by way of data- attributes
Eg:
Andrea Coiutti
@ACXgit
Jul 22 20:33
We've built sites with isomorphic architecture with Mithril, having more than 95% of the cose shared between server and client
spacejack
@spacejack
Jul 22 20:33
<div class="app-one" data-id="123" data-foo="abc"></div>
This worked really well for sites where we had Java/C# (non-JS) back-ends
So JS would only manipulate the DOM within those containers.
And we avoided any kind of jQuery-like DOM patching
Andrea Coiutti
@ACXgit
Jul 22 20:36
in these days I'm also working on a drop-in JS plugin for reservation/payment which will be integrated on the Ryanair Rooms site :smile: similar approach as @spacejack
with a bundle size of 1/3 compared to any similar jQuery-based solution
spacejack
@spacejack
Jul 22 20:38
Yeah exactly :)
Here's a sample of our component initialization:
const components = [
    {
        selector: '#component-app',
        component: App
    },
    {
        selector: '#component-summary-charts',
        component: SummaryCharts
    },
    {
        selector: '#component-home',
        component: Home
    }
]

///

for (const {selector, component, validate} of components) {
    for (const el of Array.from(document.querySelectorAll(selector))) {
        let attrs = {...el.dataset}
        if (validate) {
            try {
                attrs = validate(attrs)
            } catch (err) {
                // Validation problem. Show an error message instead of component.
                console.warn(`Invalid data- attributes for component element (${selector}): ${err.message}`)
                m.mount(el, {
                    view: () => m('.component-error', 'Component error: ' + err.message)
                })
                continue
            }
        }
        // All ok. Mount this component.
        m.mount(el, {
            view: () => m(component, attrs)
        })
    }
}
So that runs on every page load. All mithril components were in the same JS bundle. So they could also share global state if needed.
If there are no containers found on the page, nothing is mounted
Gilbert
@gilbert
Jul 22 21:09

@JAForbes: or we have no methods just streams, and we return a function which is the view.

yes please :smiley:

Dominic Gannaway
@trueadm
Jul 22 22:28
@barneycarroll I see you referenced my tweet here and someone reached out to ask more from this community. My tweet regarding virtual DOM was purely aimed at React really. It wasn't aimed at Mithril or Inferno etc.
I'm deep in the rabbit hole of basically redesigned a future world of React, where you can write a component and copy and paste it between web, iOS, Android, Windows, Mac OS, Linux and it just works 100% the same everywhere.
Plus also leveraging the things that newer frameworks are now providing for free, i.e. SwiftUI, Flutter and Android Jetpack.
Yatekii
@Yatekii
Jul 22 22:31
folks, what bundlers do you use nowadays?
and I just thought about using https://ui.toast.com/tui-editor/ . what do y'all think about it?
Dominic Gannaway
@trueadm
Jul 22 22:32
One of these wild ideas if removing HTML and DOM events from React and moving to a higher abstraction so the engineer no longer needs worry about these. They use a React Native/SwiftUI like set of core components, with a new event system that just works the same everywhere. The benefit being is that accessibility is 100% baked in, it's faster than a user abstraction, can be easily AOT compiled, uses less memory and is optimized for heavy performance. The biggest priniciple is that is scales better for all classes of application.
It's all very theoretical right now and it may never work. However, this is largely already being used on the FB5 website that will be out later this year.
One thing I introduced recently to React (behind a flag) was React Fundamental.
Patrik Johnson
@orbitbot
Jul 22 22:36
@trueadm is this related to the hermes engine somehow?
Dominic Gannaway
@trueadm
Jul 22 22:36
This allows one to expose an interface for building, updating and unmounting host components:
const ButtonImpl = {
  getInstance(props) {
    const button = document.createElement('div'):
    button.tabIndex = 0;
    button.textContent = props.children;
    return button;
  },
  onUpdate(instance, lastProps, nextProps) {
    if (lastProps.children !== nextProps.children) {
      instance.textContent = nextProps.children;
    }
  }
}

const Button = React.createFundamental(ButtonImpl);

<Button>Click me!</Button>
Nope, it's nothing to do with it.
Patrik Johnson
@orbitbot
Jul 22 22:36
:+1:
Dominic Gannaway
@trueadm
Jul 22 22:37
Although we do plan on exploring re-writing React in Rust and maybe building optimized opcodes into Hermes and then using WA for Web.
But that's probably a year or two out.

In relation to my code example above: you may be asking, why is this different from virtual DOM? Well, the importance is that this is much faster for all cases - as Svelte has shown. Plus it avoids the murky pattern of having polymorphic host components. The props shape should be monomorphic and have a strict struct-like acceptance of maybe 10-20 props. You can't spread random shit in. We do this on the DOM with HTML today because that's what we've grown to know.

It's the same with events, using DOM events is a really poor UX in most cases. "click" doesn't work the same with touch events with a pen, stylus as it does with mouse events. Not to mention it doesn't work correctly across browsers and with accessibility inputs. So you use the declarative nature of React and React Flare to provide these:

import { usePressListener, PressResponder } from 'react-events/press';

function PressableButton({ onPress, onPressChange }) {
  usePressListener({ onPress, onPressChange });
  return <Button responders={<PressResonder />} />
}
now Pressing works the same in every browser, platform, device, input and is fully a11y
Dominic Gannaway
@trueadm
Jul 22 22:43
The great thing with this, is that the implementation to handle all this logic is different for each platform, so for the web it uses passive event listeners directly with a new React event system that works with concurrent rendering. You only pay the cost of these as you use them, and they're definable in user-space - not shipped as part of React. If you do this right, you enable native-like performance and interactions on the web. If you stick with using click and other emulated web events, your app will forever feel like a web SPA, not like a native app.
I really feel this is how every framework should be going towards. So I'm trying my best to change the landscape once again and show these great practices with code and workable examples, rather than by Tweets.
I know you folks are more switched on than other communities, so hopefully you can see where I'm going with this :)
Dominic Gannaway
@trueadm
Jul 22 22:54
Side note: I think I mentioned something similar here before in the past, but couldn't find it. Gitter's search tool is absolutely terrible compared to Discord/Slack. I really wish they'd improve it.
Oscar
@osban
Jul 22 22:56
@trueadm if you know roughly when you posted it, you could try New Gitter Search (it still needs a more permanent place)
Gilbert
@gilbert
Jul 22 22:58
how does styling fit into all this @trueadm ?
Isiah Meadows
@isiahmeadows
Jul 22 23:01

@trueadm In my Mithril redesign, I've been pushing in a similar direction to what you've been suggesting, too. Events go through a separate channel abstracted from the native platform so you can sugar over them to your heart's content. It's not AOT-compiled, but it will share many of the same benefits by not updating by default. It's all stream-oriented, so that's how you update trees. I've considered also exposing an interface for low-level node management, but I've been looking at it from the context of vnodes rather than components. And of course, one big goal of mine is that you shouldn't be doing much more than import {m} from "mithril".

Separately, I recently saw this and it got me thinking of how it could be brought into JS somehow. That'd further abstract frameworks from the underlying renderer, which seems awfully compelling if you think about it. Like if createElement was itself an effect you could hook into? I don't yet have this coded into a syntax proposal, but I plan to do that some time soon.

Dominic Gannaway
@trueadm
Jul 22 23:02
@gilbert Glad you asked! We don't have a set-in-stone answer for this. We envisage composing styles using components rather than CSS, but one nice thing about fundamental components (like I showed above) is you can leverage inline styles with the performance of stylesheets.
I found a really nice perf hack that no one has stumbled across yet, you use cloneNode.
So any static styles, you extract and set them inline, save that template, then later clone that node and apply the delta on updates. This would be impossible with virtual DOM, possible with a compiler, but much easier with an exposed DOM interface component like React Fundamental.
@isiahmeadows That's interesting. I guess we are all realizing that events are really a separate paradigm to "elements" and chucking events as a contract to each node just doesn't always make sense.
With React Flare (the new React event system I've been working on), we don't need an AOT compiler, but rather there's two new concepts:
  • listeners
  • responders
you attach responders to a host node, like a <div>
and then you can have responders (using hooks) anywhere in your tree that can listen to events fired from the responders
what this means, is that you're not listening to DOM events, but rather custom events. Responders listen for actual DOM events, then fire custom events for your app
this allows you to compose events into gestures
useDragAndDrag
useLongPressThenDrag
Dominic Gannaway
@trueadm
Jul 22 23:10
these hooks actually do some clever things behind the scenes, which is all very declarative and easy to read:
function useLongPressThenDrag() {
  const [isDragging, updateDragging] = useState(false);
  useSequence([
    useLongPress({ delay: 500 }),
    useDrag({ onDragChange: updateDragging }),
  ]).onRelease(() => updateDragging(false));
}
something along those lines
it all boils down to complex state machines, like this: https://gist.github.com/necolas/5f3f987e69a0db481f67c29fd5e251f7
but if you wrap it in a re-usable hook, then everyone can benefit from it without needing to know about it
and it works on the web, iOS, Android etc... like it just works
Isiah Meadows
@isiahmeadows
Jul 22 23:12

I've been looking at a more direct reducer-like model: on: [receiver, ...eventNames]. The receive is called as receive(ev) where you use ev.type to tell events apart. A receiver callback might look like this:

function receiver(ev) {
    if (ev.type === "click") {
        if (ev.target.tagName === "input") {
            const completed = !todo.completed
            State.dispatch({type: "setStatus", todo, completed})
        } else {
            State.dispatch({type: "destroy", todo})
        }
    } else if (ev.type === "dblclick") {
        State.dispatch({type: "edit", todo})
    } else if (ev.type === "keyup" || ev.type === "blur") {
        if (ev.keyCode === 13 || ev.type === "blur") {
            State.dispatch({type: "update", title: input.value})
        } else if (ev.keyCode === 27) {
            State.dispatch({type: "reset"})
        }
    }
}

// And where it's used
return m("li", {
    class: (todo.completed ? "completed" : "") + " " +
        (todo === state.editing ? "editing" : ""),
}, [
    m("div.view", [
        m("input.toggle[type=checkbox]", {
            checked: todo.completed,
            on: [receiver, "click"],
        }),
        m("label", {on: [receiver, "dblclick"]}, todo.title),
        m("button.destroy", {on: [receiver, "click"]}),
    ]),
    m("input.edit", {on: [receiver, "keyup", "blur"], ref: inputRef}),
])

And of course, listening from components works the same way: m(NewThread, {on: [onSave, "save"]}).

It's still coupled to conceptual "nodes" (both elements and components can emit them), but it's not directly coupled to the DOM.

Dominic Gannaway
@trueadm
Jul 22 23:12
^^ you might one to see that gist
that's from Nicolas, he wrote React Native Web
it's very similar to what you have, but higher abstraction
the whole point in Flare is that you don't need to use DOM events, because they're not reliable. Especially focus
For Focus, we expect React to completely override to browser's focus system. In doing so we've found it to be faster and more reliable for screen readers and a11y than all browsers.
Ironic really.
Plus it works with Suspense and Portals.
We've adopted the same a11y system that iOS and Andorid use, which is much more refined than the web's legacy and historic shit show
Isiah Meadows
@isiahmeadows
Jul 22 23:15
Makes sense. I've been more focused on building the primitives to start, then building from there.
Barney Carroll
@barneycarroll
Jul 22 23:15

@CreaturesInUnitards

In that light, we see that lIfecycle methods are really just sugar, but nothing about them necessitates having a view.

YES

So if we think of a component as ( view && lifecycle ) || view || lifecycle, and think of a view as model => view, we could agree that a component may:

  1. simply inform the model, or
  2. simply create the view based on the model, or
  3. both.

'Informing the model' can do a lot of heavy lifting - I think @trueadm has put more work into that space than most. But as a UI engineer targetting DOM, I'm largely untroubled by the events system - except when it pisses me offf

Dominic Gannaway
@trueadm
Jul 22 23:17
@isiahmeadows For example, we have an InputResponder.
const [value, updateValue] = useState('');
useInputResponder({ onValueChange: updateValue });

return <input value={value} responders={<InputResponder />} />
works regardless of input type, taken from the existing ReactDOM ChangePlugin
but you basically have two way data-binding in 2 lines of code
without it being two way data binding... mind blown!
^^ with the above
you'd probably wrap the 2 lines into a custom hook
const value = useControlledInput():
return <input value={value} responders={<InputResponder />} />
but no one actually writes their own inputs in apps, they use a shared component
so it would really be:
const value = useControlledInput():
return <TextField value={value} />
and that is where the elegance lies, in abstraction!
Isiah Meadows
@isiahmeadows
Jul 22 23:21

@trueadm If this helps, my redesign is following a more imperative functional model rather than the declarative cell-based model of React Hooks. So that should probably explain the architectural differences.

but you basically have two way data-binding in 2 lines of code
without it being two way data binding... mind blown!

It's worth noting Mithril's had that simple fake data-binding idiom since well before v1. We just decided to make it more explicit over time.

My redesign is still a bit of a hybrid, but it's saying "make the tree look like this", not "this is what the tree looks like".
Dominic Gannaway
@trueadm
Jul 22 23:22
this isn't data binding really though, I just called that because it's as simple as that.
Isiah Meadows
@isiahmeadows
Jul 22 23:22
I get that. I called it fake for a reason. :wink:
Dominic Gannaway
@trueadm
Jul 22 23:22
I think that your idea makes sense, just you want to abstract it more
Like I said, no one who builds apps that scale writes their own <input /> elements
They have a MyCompantAppTextField
in there they having tracking shit, a11y, styling, etc
so if you break out the event system so you're saying
function MyApp() {
  const pressed = usePressed():
  return <MyAppButton>{pressed  ? 'Okay you can let go' : 'Press me now!'}</MyAppButton>
}
So I built this for the new FB5 website, and iternally the reception has been huge
many see React Flare as bigger than any other React feature we've released in the last few years, beacuse it makes hooks feel so important and makes components feel so alive
plus, it has a built in system that makes performance unparalleled to the old DOM event system
that's my secret sauce
Isiah Meadows
@isiahmeadows
Jul 22 23:30

I get that. I just want to approach it cautiously because I've found "do X" is a bit more intuitive than "this is X" and I'd like to make sure it fits into my model. Trust me in that I do see the value of it, and I did draw a lot of inspiration from hooks as well.

I will note one common gotcha with hooks: it's difficult to diff past objects, which is a problem for interactive components and I've already run into that a few times with private tech demos.

We're much more conservative with new features, but you probably already knew that.
Dominic Gannaway
@trueadm
Jul 22 23:31
With React Flare, you don't need to pass down props or use context or use memo.
useListener will trigger on any events in any of its children firing
it dramatically changes how people write React apps and hooks. Write now it's very boilerplatey around callbacks
Isiah Meadows
@isiahmeadows
Jul 22 23:33
Oh okay. I do wonder: how do you track which events to subscribe to?
Dominic Gannaway
@trueadm
Jul 22 23:33
Responders listen for DOM events
and have their own state machine
Isiah Meadows
@isiahmeadows
Jul 22 23:33
Yeah, but how do those know which DOM events to listen to?
Dominic Gannaway
@trueadm
Jul 22 23:33
here's the HoverResponder
Flare responders aren't React components, they are a new primitive, where they have their own state and have low-level access to the DOM API. They have a communication channel back to React so they can co-ordinate dispatching of events and usage of helper methods
Isiah Meadows
@isiahmeadows
Jul 22 23:35
I will throw this out, in case it wasn't clear initially: I'm aiming for a solution that is simple for small things, but scales infinitely well. Easier said than done, and I've spent at least half a year trying to iron this out.
Dominic Gannaway
@trueadm
Jul 22 23:35
I think we're aiming for the same thing
the idea behind Flare is to be super simple
we, the React Core team, will ship many core responders as part of React
Isiah Meadows
@isiahmeadows
Jul 22 23:36
I can see how that model of "emit everything" could be useful. Quick question: how do you track things like keys. Suppose you have a list of todos and you need to know which todo was clicked. How would you figure that out?
Dominic Gannaway
@trueadm
Jul 22 23:36
Press, LongPress, Hover, Focus, FocusWithin, FocusContain, Drag, Swipe, Pan, Scroll, Input
Isiah Meadows
@isiahmeadows
Jul 22 23:38
Not what emits or captures the events, but how do you figure out the identity of where the event came from?
Think: something like this:
todos.map(t => <Todo key={t.id} todo={t} />)
Gilbert
@gilbert
Jul 22 23:39
e.currentTarget? :smile:
Dominic Gannaway
@trueadm
Jul 22 23:39
@isiahmeadows There are many ways, the simplest are handles:
const NameHandle = createHandle();
const AgeHandle = createHandle();

function MyForm() {
  <FormItem value={state.name} responders={<InputResponder handle={NameHandle} />} />
  <FormItem value={state.age} responders={<InputResponder handle={AgeHandle} />} />
}
Isiah Meadows
@isiahmeadows
Jul 22 23:39
@gilbert Only works if it's a DOM element emitting it. Could be a synthetic component event. :wink:
Gilbert
@gilbert
Jul 22 23:39
yes, I was joking :)
Dominic Gannaway
@trueadm
Jul 22 23:40
But... I even suggested another concept of "signals"
which, let me get a Gist
Isiah Meadows
@isiahmeadows
Jul 22 23:40
@trueadm What about dynamic lists? Can you create a dynamic list of createHandle()s?
Dominic Gannaway
@trueadm
Jul 22 23:43
I can't find the gist for signals, but it was basically a declarative way of formalizing data via the fiber tree.
Isiah Meadows
@isiahmeadows
Jul 22 23:44
Could you show some code to help me see how that'd work?
@trueadm
Isiah Meadows
@isiahmeadows
Jul 22 23:49
I'm not quite sure what you're referring to.
Barney Carroll
@barneycarroll
Jul 22 23:50
@trueadm I really like your events origin pattern!
Dominic Gannaway
@trueadm
Jul 22 23:52
Sure, signals was designed around the case you mention:
const FormSignal = createSignal(InputResponder);

function MyForm() {
  useSignal(FormSignal, { onValueChange(e, signal) {
    console.log(signal.id);
  }});

  return <ListOfFormItems />
}

function ListOfFormItems() {
  return items.map((item) => (
    <FormSignal key={item.id} onValueChange={(value, signal) => {return {id: item.id, value}})}>
      <FormItem value={item.value} responder={<InputResponder />} />
    </FormSignal>
  ))
}
basically signals connect to events (or whatever you provide as a responder)
when comething fires in your component tree, they connect onto that event and trickle up the tree, some form of upwards data
which can then be captured in an event listener
I had a really detailed example where I basically solved forms on the internet, but it's internal and full of Facebook IP
I'll tidy that up and maybe post about it in the coming weeks!
I'm basically on a journey to make events declarative, kind of like how React made elements and components declarative 8 years ago
today, we think of events as imperative side-effectful mess
imagine never writting another callback event again! you pass declarative signals through the tree, that compose and reduce themselves as they propagate!
Fred Daoud
@foxdonut
Jul 22 23:57
@trueadm may I say, it's always nice when you come for a visit :) :thumbsup:
Dominic Gannaway
@trueadm
Jul 22 23:58
How come?
Fred Daoud
@foxdonut
Jul 22 23:58
how come it's nice?
Barney Carroll
@barneycarroll
Jul 22 23:58
@trueadm thanks so much for sharing that!
Fred Daoud
@foxdonut
Jul 22 23:59
because you have great ideas and you share them with us and I find them interesting to read!