Closed BrandonDyer64 closed 2 months ago
We don't have hook rules.
This example
Effect::new(move |_| {
RwSignal::new(0); // <- bad but I wouldn't necessarily know it
});
is completely fine, and is a microcosm of how the whole renderer works.
I just want to clarify that there's nothing wrong with the examples you're giving as wrong, as long as you understand the ownership paradigm/when things will be disposed, so I'm not sure I understand what the helpful hint is supposed to be that the naming conventions give.
What exactly is the ownership model for that then? I assume the effect is tied to the component it's called in and will be disposed of when the component (or its view) drops, but the enclosed signal is not created in such a context, so when is it destroyed?
In my original draft of my post I actually didn't talk about it being bad to put things in particular places, so I suppose I'll still just lean on that then, which is the idea that having functions with a consistent naming convention is still better because it communicates to the user that this will insert data into the reactive graph.
What exactly is the ownership model for that then? I assume the effect is tied to the component it's called in and will be disposed of when the component (or its view) drops, but the enclosed signal is not created in such a context, so when is it destroyed?
There's a pretty complete description of how ownership works here. The short answer is that it is a tree of effects. So your RwSignal
is owned by your Effect
, and your Effect
is owned by whatever render effect created that component, or by the root if it's at the root. When that component is unmounted/its parent re-runs, then yes both the Effect
and the RwSignal
will be disposed, because it is a parent-child relationship. But the component doesn't own anything; there's nothing wrong with having a signal created inside an effect. It will be disposed whenever that effect re-runs, or when the effect is disposed.
having functions with a consistent naming convention is still better because it communicates to the user that this will insert data into the reactive graph.
This is a less precise concept than you might think! Do you mean "insert data into the arena"? (the thing that makes things Copy
) Or "insert things into the reactive graph"? Neither create_rw_signal
nor RwSignal::new()
inserts anything into the reactive graph; it does insert data into the arena.
===
I'm fine with "I disagree with this as a matter of style" and open to other feedback, to be clear.
Thanks for your quick feedback
The short answer is that it is a tree of effects. So your RwSignal is owned by your Effect, and your Effect is owned by whatever render effect created that component, or by the root if it's at the root.
Ah, I see. Out of curiosity, would it be fine to call RwSignal::new()
in a callback or a resource's async block (wouldn't that put it on the root)?
Do you mean "insert data into the arena"? (the thing that makes things Copy) Or "insert things into the reactive graph"? Neither create_rw_signal nor RwSignal::new() inserts anything into the reactive graph; it does insert data into the arena.
I understand that the data given to the signal is put into an arena, but I meant that it becomes part of the reactive graph in the sense that the runtime now tracks it as a source, and will cause effects to become dirty upon being changed. Regardless, I just mean that "some side effect is being created and it has to do with reactivity."
Sorry for the delayed response.
would it be fine to call
RwSignal::new()
in a callback or a resource's async block (wouldn't that put it on the root)?
That is correct, it does that, and you can see the implications in the difference between the counters
example in 0.7/main branch (uses ArcRwSignal
to avoid leaking), and in 0.6 (uses manual .dispose()
to avoid leaking.
I meant that it becomes part of the reactive graph in the sense that the runtime now tracks it as a source
Creating a signal doesn't cause the runtime to do anything with it, it just creates a struct and stores it in an arena. Reading a signal is what causes the special reactive behavior. So the creation itself does not have side effects on the reactive system, and doesn't insert the signal into the reactive graph, which is why I was clarifying.
I'm going to close this as "not planned" but thanks for the discussion.
Perhaps this is a bit of a hot take, and I know the library is just getting switched off of the
create_*
paradigm but, in my humble opinion, this is a step in the wrong direction. I really love Leptos, and this is more of a nitpick than anything, but I thought I should at least vocalize how I see this particular aspect.I always thought that the old paradigm made it clear where different functions should be run. Every struct under the sun in Rust has the
::new()
associated function, but effects and signals should only be created in certain, specific contexts. Namely, inside of components and not inside of reactive closures. When I see a function that starts withcreate
,use
, orprovide
, I think "ah, this is a hook, and it's going to put something on the reactive graph. I better be careful where I call this."It also keeps the hooks inline with custom hooks, and gives custom hooks a strong convention to follow. Consider the following:
These are all following a consistent pattern, but the following are not:
Some things are struct invocations, some are functions, some are obvious hooks. I disagree with the statement that the second example is "more rusty" as there's nothing inherently unrusty about functions. And, should custom hooks continue using the old naming convention? For libraries (including leptos itself) how can one tell the difference between something that should be called as a hook and things that merely perform an action?
I'd actually argue it should go in the other direction and become even more consistent, opting for only
use_*
and doing away withcreate_*
entirely. This means one can search for all hooks with a simple search foruse_
on docs.rs and can be sure something is a hook with one simple glance.I know it's probably too late to contribute to the conversation on this topic, and unlikely to change your mind, but I think - at the very least - the hook functions should remain available for those who want them. Again, Leptos is awesome and keep up the amazing work!