Comments (19)
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
createFlipState
createAnimation
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,
from solid.
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.
from solid.
Ok moved into a repo: https://github.com/ryansolid/solid-transition-group
from solid.
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.
from solid.
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
afterEffects - runs after synchronous graph updates (current implementation is a bit simple deferring to next microtask)
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.
from solid.
Ok I believe I've done the Scoreboard now.
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 transition: 3500ms;
to .view1 .removed {}
to observe that it still works with variable timings.
from solid.
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.
from solid.
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.
in domvm this isn't actually done in 2 passes either. the idea is that physical dom removal can be deferred/queued by any willRemove
or willUnmount
hook that returns a Promise. any followup redraws can see if an encountered dom node is in the removal queue and skip over it.
from solid.
@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 parent.textContent =""
would have to switch the the less optimized method. You'd have to know down the whole tree before making that decision. It could come down to owning context. Any descendant with a registered transition handler would signify non-optimized execution path. I think Svelte handles things similarly.
from solid.
Any descendant with a registered transition handler would signify non-optimized execution path.
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
from solid.
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.
from solid.
yes. since the presence of will*
removal hooks taints all ancestors, they will await resolution of any descendant removal promises. admittedly, the bookkeeping is imperfect because it's expensive, so the de-optimized removal flags may remain on ancestors if a leaf component is explicitly redrawn in isolation.
notably, did*
removal hooks will not de-optimize since they have no way of preventing removal, so usually it's in those that user-defined cleanup/teardown should be done.
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
from solid.
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.
from solid.
Just started playing with Solid and I had the same desire for mount/unmount animations.
I took inspiration from Framer Motion and ultimately just followed this quite closely. I may be naive but is this suitable? (Using anime.js as my pref but inline animation from link above works too)
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>
);
}
from solid.
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 signal
. Admittedly that is the one thing always awkward about demos. In real applications I use state
way more. But for simple piping signals are great.
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 createMemo
so that they are cached unless changed from the outside.
from solid.
@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.
from solid.
I think a valuable example would look something like this:
- Here's a list of items
- Now, here's how you add animations when an item is added/deleted
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.
from solid.
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.
from solid.
@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
from solid.
Related Issues (20)
- Typo found in signal.ts? HOT 3
- Circular references in Stores trigger loss of proxying if non-trackable objects are referenced HOT 5
- Changing the "data-type" of a `createStore` attribute causes referenced data to be modified HOT 1
- `value` parameter of `createEffect` is not passed to `on` as `prevValue` when `defer` is `true`. HOT 1
- using createStore setter function to update array results in argument is not iterable error HOT 3
- Solidjs is not working with bun.
- Laggy Performance HOT 4
- Unable to pass signal to child components HOT 1
- ReferenceError: XXX is not defined when using createResource HOT 3
- 2-way binding for computationally expensive effects
- `createResource` seems to fail to trigger fetcher when source signal changes HOT 1
- Why can't my code use context? HOT 9
- Setting properties to the unwrapped value of a store doesn't always propagate into the store HOT 14
- If a function wrapped in `action` throws some error it should be possible to `catch` it. HOT 1
- Isomorphic export with `solid-js/web` HOT 3
- Feature request: package entry point for reactive core HOT 1
- Trying to access fields of data from `createResource` throws error HOT 2
- Why the element between Shows is removed? HOT 4
- useTransition pending states should be isolated in different Suspense boundaries HOT 2
- 'reconcile' mutates array items HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from solid.