on const div = <div/>
returning a div
#4
Replies: 3 comments 3 replies
-
Hey @titoBouzout, thanks for making this conversation official! Although I already know, could you please update the post so that it also gives a brief example on how the function return value (regardless if JSX or |
Beta Was this translation helpful? Give feedback.
-
I believe that this is a totally fine thing to do, as long as you are aware that you are executing a reactive context, and the caveat mentioned in the OP that if you pass this div into a template then it will not be able to see contexts. If my use case were the following, then it is totally legit because I'm not needing anything more from it, I am not distancing myself from how things work because it works exactly as expected (with Solid 1.9): <!-- My app is just a simple HTML file -->
<script type="importmap">...define URLs for dependencies...</script>
<script type="module">
import {createSignal, createEffect} from 'solid-js'
import html from 'solid-js/html'
const [count, setCount] = createSignal(0)
doSomethingWithState()
function doSomethingWithState() {
setInterval(() => setCount(c => c + 1), 1000)
}
const div = html`<div>Some app with state: {count}</div>`
document.body.append(div)
</script> That example does exactly what I need and I haven't broken anything. However, I understand that, if people start to write more complex apps, and for example they copy/paste some code around and are end up making standalone elements, then start to interpolate those elements into some other templates, and begin to use contexts (that's a bit of requirements), then things may not work as expected. Solution(s)?I'm not sure all of the following is worth it, but just sharing ideas that could solve the problem: Maybe simply always returning a function would allow people to break up code into pieces, while not immediately executing things, and making it more obvious that if a function is called then a component is being executed on its own with its own reactive context: const div = html`<div>Some app with state: {count}</div>`
document.body.append(div()) // intentionally execute the function, a little more obvious perhaps In this case, maybe we can even do something extra: maybe we could return a memo for the top level value of the template? Just an idea, thought food: const [divTemplate, div] = html`<div>Some app with state: {count}</div>`
createEffect(() => {
if (!div()) return // timing of effects does not matter with this approach, you just wait for the div.
doSomethingWithElement(div())
})
document.body.append(divTmpl()) The template would be 1-to-1 with source invocations. So calling function Foo() {
const [divTemplate, div] = html`<div>Some app with state: {count}</div>`
createEffect(() => {
if (!div()) return // timing of effects does not matter with this approach, you just wait for the div.
doSomethingWithElement(div())
})
return divTemplate
}
const tmpl1 = Foo()
const tmpl2 = Foo()
document.body.append(tmpl1())
document.body.append(tmpl2()) With this approach, the memo would always be available from the return value of a template, allowing a pattern that doesn't break contexts while allowing people to break templates up into arbitrary pieces like this: function MySolidComponent() {
const store = createStore({...})
// one template and its effect grouped together.
const [headerTmpl, headerEl] = <header>Some value: {store.someValue}</header>
createEffect(() => {
if (!headerEl()) return
doSomethingWithHeader(headerEl())
})
// another template and its effect grouped together
const [mainTmpl, mainEl] = <main>Some data: {store.someData}</main>
createEffect(() => mainEl() && doSomethingWithMain(mainEl))
// Finally, the return value of MySolidComponent
return <div>
<SomeContext>
{/* Context works! */}
{headerTmpl}
{mainTmpl}
</SomeContext>
</div>
} This would make it possible for the code to be organized like this: function MySolidComponent() {
const store = createStore({...})
const [headerTmpl] = header()
const [mainTmpl] = main()
// Finally, the return value of MySolidComponent
return <div>
<SomeContext>
{/* Context works! */}
{headerTmpl}
{mainTmpl}
</SomeContext>
</div>
}
function header() {
// one template and its effect grouped together.
const [headerTmpl, headerEl] = <header>Some value: {store.someValue}</header>
createEffect(() => {
if (!headerEl()) return
doSomethingWithHeader(headerEl())
})
return [headerTmpl, headerEl]
}
function main() {
// another template and its effect grouped together
const [mainTmpl, mainEl] = <main>Some data: {store.someData}</main>
createEffect(() => mainEl() && doSomethingWithMain(mainEl))
return [mainTmpl, mainEl]
} But at this point we basically have another way to write components, so hmmmmmmm. More solutions?Another idea is that Solid template could track contexts based on DOM references. Then there would be no need to change the API at all, but implementation may be more complex: const div = <div>Some signal: {someSignal}</div>
document.body.append(
<div>
<SomeContext>
{/* context will work, Solid would use DOM references to associate the context with the
outer div, and/or it would also detect when other elements are interpolated into locations
that are under one of those contexts. */}
{div}
<SomeContext>
<div>
) |
Beta Was this translation helpful? Give feedback.
-
The No Build comment was about people returning functions in functions that aren't memo'd. Early days I put in code to invidually wrap and unwrap that. But I realized I could skip doing so if people always wrote code a certain way. The JSX compiler and pre-made control flow pretty much ensures this. Especially considering linting and TS. By removing accepting functions as allowed and having the compiler/Components provide them there is no opportunity to go wrong. My concern is that No Build loses those safe guards. Not that it executes differently. That you can write code that could cause problems. |
Beta Was this translation helpful? Give feedback.
-
Opening this discussion to record this topic in an open place.
Description
The following code
const div = <div/>
returns aHTMLDivElement
. This isconsidered nice but there are problems with it.
I've spoken with @trusktr many times about it. It is the dream for things to
just be elements, but in practice it doesnt align with how things are structured
and work.
Problems
Consistency
The issue that bothers me the most about returning elements, is that is not
consistent with the rest. It creates the illusion that things just work till
your expectations break, and makes some use cases impossible.
div
it's actually a div, it has been rendered outside the return statement.For the time
Context
evaluate its children, it only seesHTMLDivElement
. Theexpectation is that
value
is1
in the return, but value is actually0
.Why a function changes everything:
The tree is evaluated from parent to children, instead of from children to
parent:
The difference between:
and
When the first example evaluates, it receives a function as argument. The second
example executes the function before the
createComponent
call, thereforeevaluating children before its parents, and this is the whole problem.
If
div
were a function instead.. then the vardiv
would be holding afunction, and
Context.children
would be evaluated after creatingContext
not before. (I am aware that I am reiterating in reverse what I said in the
previous sentence, I am trying to make the issue clear)
Aside Note: This problem is sometimes manifested when using the children helper
children(() => props.children)
. This case has a fix: Re-parenting. 1. Becausethe children helper its actually a function, so it can be run at will. 2. By
making of context a memo that tracks owner as a dynamic value that points to
context.
I would like to put emphasis on that breaking expectations is a let down that
should be avoided when possible.
Contextual Information
In a similar way to the problem described in point 1. It is mentioned here that
makes things more difficult.
https://hackmd.io/@0u1u3zEAQAO0iYWVAStEvw/HJG_uXI20
Unexpected uses
Illustrates that those who have a
div
will be tempted to use it in ways theplatform allows, distancing themselves from how things are supposed to work.
No build use case
The quote is from https://hackmd.io/@0u1u3zEAQAO0iYWVAStEvw/HkV5eNkyR
I found the quote confusing. no build shouldn't change anything about how
things run. It will run the same as JSX. No build, doesn't mean stuff needs to
be elements, it just means that there's no build step. (More of this on the next
point)
The
html
functionThat's also fundamentally broken if it returns a
div
(I used to also allowreturning elements for this function in pota, but I
removed that "behavior" in a recent version, now it only returns function, or an
array of functions).
As I see it, despite the name,
html
is a convenient way to parse html markupmixed with js without shipping a full-blown multi megabyte babel transform to
the browser.
There should be no difference between the following
div1
anddiv2
:Pros
The use cases presented in favor of returning a
div
are to simplistic, whichis fine I guess. I think you can just do the following then, for simple demos
and things, it's actually nice, and not complicated to do, but you are on your
own with what you do with
div
.I guess one could get far by using elements this way, but that's tracing
distance with the provided solutions Solid gives. Using the system with a
different "mindset" will eventually cause problems, given both have 2 different
sets of assumptions.
The unaware
I remember myself saying "I love that solid returns divs", but I said that
because I didn't understand at the time how stuff works with reactivity, and I
admit I was doing things wrong.
Bottom
I just think that the idea of returning
divs
doesn't align with the philosophythat stuff it's encapsulated in functions, and is reactive because functions are
the actual thing being tracked, to re-run at will and derive things.
Maybe this issue is just a reflection of a deeper problem. Solid tries to hard,
and unsuccessfully to hide functions from props, JSX, children, html fn, refs,
etc.
Related: The discussion about divs being divs been around for a while and
recently started in, which has @fabiospampinato and @thetarnav input too
https://discord.com/channels/722131463138705510/780502110772658196/1280988507980042411
Beta Was this translation helpful? Give feedback.
All reactions