-
-
Notifications
You must be signed in to change notification settings - Fork 952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lifecycles & intro/outro transitions demos #39
Comments
That's good idea. My CSS has always been a bit weaker on my demos so far so I can look at polishing that up a bit. I've also been considering adding transitions via the compiler like Svelte but I'm not there just yet. Something like the Scoreboard makes sense and I can look at doing something like that. Control flows have afterRender hooks which is how I used to solve this in Knockout. Lifecycle functions aren't so explicit. They are more like React Hooks (thank god for them otherwise I'd be arguing with people for hours about their necessity). There is no lifecycle around rendering specifically just 2 hooks at your disposal: onCleanup - runs before next invocation to cleanup, or when parent context is destroyed These can be used inside any other effect/memo to schedule work to be done. To achieve the same similar to React useEffect and useLayoutEffect. So everything is very fine grained there is no absolute did_____. I need think of an example that does better at showing this off. |
Ok I believe I've done the Scoreboard now. https://codesandbox.io/s/solid-scoreboard-sjpje I still not sure how to show off lifecycles since I don't really have any. If anything the Scoreboard example has all the lifecycle Solid has. I was trying to think of a scenario with nested cleanup but there is nothing fancy about it. I think what I really need to do is write a basic tutorial on the fundamentals of fine grained reactive programming. It takes a little bit to think this way, which I take for granted since after nearly a decade it is the way I think about solving any problem. |
nice. though i think lifecycle-hooks is actually the more interesting case here since it has to retain dom elements that have been removed from state to ensure the outro transitions complete. also discerning a reorder/reinsert from a delete/recreate in new position. while my demo does not show this, the actions following the removal do not rely on the outro finishing, it's just timed that way so as not to be too seizure-inducing. you can add |
Ok yeah that is interesting. I currently have nothing of that nature. I'd even be interested to see such an example in React since I don't think they have anything like that either. What you'd do with React is where I'd first start. Although I know Svelte also handles those sort of cases pretty well. Looking at domvm in the example you handle that on a per node basis which is interesting. Native DOM nodes having lifecycle hooks could be powerful. This actually came up in #36. At a minimum connected, disconnected, reconnected seems viable, although I suppose willDisconnect is the more interesting case. The first 3 could be handled in a generic way independent of the Solid and work with any library. But knowing the intent to remove could only be handled in Solid's Control flow (conditional/list reconcilers). Ironically this would have been a lot easier when I did the updates in 2 passes but since I optimized to a single pass this actually is can't simply be a single hook to compare node lists before and after and asynchronously resolve completion. Mind you that optimization actually was almost unnoticeable from execution time perspective so perhaps if this is important enough I could go that way. Honestly I'm not sure. I know some Virtual DOM libraries have done a lot of research in this area, and it's definitely an area that I think fine grained computations could do some really powerful stuff. I'm just unsure where it fits in the priorities. For now I think to be pragmatic, I'm going on the side of what can React do. If anyone wants to look into this sort of thing I'm all ears. |
in domvm this isn't actually done in 2 passes either. the idea is that physical dom removal can be deferred/queued by any |
@leeoniya Thanks for that. As you can probably tell from my thinking out loud I haven't really thought about it a ton. And that comment alone probably saved me some time digging. A simple check pre-removal and a flag on the indicating DOM nodes would work. The trickiest part would actually be identifying the situation where shortcut disposal is involved, since any |
yep, domvm bubbles this info up in the initial vtree normalization pass [1] [1] https://github.com/domvm/domvm/blob/master/src/view/preProc.js#L35-L37 |
Ok if you will, one more question. How do you handle nested contexts? Like if you had a conditional in a list. And the transitioning element is in that conditional. Parent removes the whole list/panel etc, do you delay doing so until the one item fades out? If there are nested removal effects do they cascade upwards? Consecutive/parallel? I honestly have never used a library that had something like this so I'm unclear on expected behavior. |
yes. since the presence of notably, honestly, i don't do too much transition/animation-heavy UIs in concert with dom removals, so there's sure to be more than a few gaps. e.g. i don't collect all promises from descendants for Promise.all() [1], and the ancestors are tainted with a boolean rather than a counter which should get decremented as the offending descendants are removed. i'm not terribly motivated to spend time on it though :) [1] https://github.com/domvm/domvm/blob/master/src/view/dom.js#L30 |
Thank you. That gives me a better idea of the complexity. Svelte has transition feature like this and constantly has issues reported on it. I'm unsure how much to invest but this has been infinitely helpful. |
I've made a handful of demos playing around with different stuff but all outside of the core library. Grabbing from various demos I found around the web: DOM Lifecycles Since these aren't core I'm not sure where they belong but they are at least in this thread for reference. There is a lot you can do with the few Hooks Solid has. It's going to take a lot more effort to get the more complicated scenarios though, |
Just started playing with Solid and I had the same desire for mount/unmount animations. import { render } from "solid-js/dom";
import { createSignal } from "solid-js";
import Fade from "./Fade";
export default () => {
const [ show, setShow ] = createSignal(false);
return (
<div>
<button onClick={() => setShow(!show())}>
{show() ? "hide" : "show"}
</button>
<Fade show={show}>
<div> HELLO </div>
</Fade>
</div>
);
};
render(() => (
<App />
), document.body); Fade.js import { createEffect, createSignal } from "solid-js";
import anime from "animejs";
export default function Fade({ show, children }) {
const state = show();
const [ shouldRender, setRender ] = createSignal(state);
createEffect(() => {
if(show()) {
setRender(true);
anime({
targets: children,
easing: "linear",
opacity: [0, 1]
});
} else {
anime({
targets: children,
easing: "linear",
opacity: [1, 0],
complete: () => {
setRender(false);
}
});
}
}, [show()]);
return (
<Show when={shouldRender()}>
{show() &&
children
}
</Show>
);
} |
This as is won't quite work with Solid due to syntax differences and that the data is reactive, but an approach like this should work. Reading the article I'd probably keep things simple for the fade component using signals. And outside we'd use whatever makes sense. But in this case being a toy demo it would be a But it would look like: index.js import { createSignal } from "solid-js";
import { render } from "solid-js/dom";
import Fade from "./fade";
const App = () => {
const [show, setShow] = createSignal(false);
return (
<>
<button onClick={() => setShow(!show())}>
{show() ? "hide" : "show"}
</button>
<Fade show={show()}>
<div> HELLO </div>
</Fade>
</>
);
};
render(App, document.body); fade.js import { createSignal, createEffect } from "solid-js";
const Fade = props => {
const [shouldRender, setRender] = createSignal(props.show),
onAnimationEnd = () => !props.show && setRender(false);
createEffect(() => props.show && setRender(true));
return (
<Show when={shouldRender()}>
<div
style={{ animation: `${props.show ? "fadeIn" : "fadeOut"} 1s` }}
onAnimationEnd={onAnimationEnd}
>
{props.children}
</div>
</Show>
);
};
export default Fade; But yeah generally you'd approach these problems the same way you do with React Hooks. Replacing with your anime library could work. Just unclear what it wants with targets. Since children are lazy evaluated I think there are some options here. But it's going to basically come down to hoisting the children. If they are static this is easy as you can just evaluate it outside of the JSX. But otherwise we might need to wrap them in a |
@ryansolid I just changed my example to use signals and started to also clean it up a bit to be more terse. Seems to be working well. I'll play around with children opts. Thanks for the quick reply. |
I think a valuable example would look something like this:
React's story here (with react-transition-group) is close to ideal. You're basically looking at a diff like this: return (
<ul>
+ <ReactTransitionGroup>
{items.map((item) => (
+ <CSSTransition>
<li>{item.text}</li>
+ </CSSTransition>
)}
+ </ReactTransitionGroup>
</ul>
); Surely this is made easier by the fact that React has a virtual dom. I haven't thought about how a Solid approach might look. |
We use list reconciliation similar to a virtual dom so the same pattern interface would work more or less. It just works basically opposite direction since changes notify up rather than re-render down. We wrap each item in an object that contains extra state data about animation state, and then feed the reconciled nodes into a parent that keeps track of all these nodes and merges them into what gets attached to the DOM. That being said it would be nice to have the means to do this less verbose ala Svelte. The challenge is that Solid's components aren't real nodes from our perspective. So not clear how to get the same level of control using directives but only on intrinsic elements. Not even sure if it would be the best pattern, but I suspect on inspection something similar should be possible. |
@whatisaphone Still WIP progress but I've mimic'd Vue's Transition/TransitionGroup here. As you can see something like this should be possible. There are some limitations in this implementation like children must be single DOM node. I still have jitteryness on transition group but I think it demonstrates the potential. https://codesandbox.io/s/basic-css-transition-36rln?file=/styles.css |
Ok moved into a repo: https://github.com/ryansolid/solid-transition-group |
I think this issue has run its course. We've demonstrated all sorts of animations with Solid including exit transitions. Examples have been added to examples page. |
hey @ryansolid
i was wondering if you could add some idiomatic demos for something like this:
https://domvm.github.io/domvm/demos/playground/#lifecycle-hooks
https://domvm.github.io/domvm/demos/playground/#scoreboard
The text was updated successfully, but these errors were encountered: