Open em opened 4 years ago
I am having a similar issue where the exit prop of an AnimatePresence's grandchildren are not triggering if those grandchildren are conditionally rendered. Adding the nested AnimatePresence to surround those conditionally rendered components should theoretically solve the issue but it does not.
In the attached example, We have code that is essentially:
<AnimatePresence>
{ shouldRender && <motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<AnimatePresence>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
There is nothing here that should orchestrate a sequential animation. It would be unexpected for these animations to not run simultaneously.
As it is, the child doesn't actually animate at all as it isn't being removed from its AnimatePresence
parent. I can potentially see a feature request for parent exits to "pass through" child AnimatePresence
components but I'd argue that that contravenes this:
those components should be able to use AnimatePresence themselves independently without caring if they happened themselves to be rendered by another AnimatePresence.
There is nothing in this bug report that suggests a child wouldn't be able to render its own AnimatePresence
. The child AnimatePresence
isn't being used in this example. So if AnimatePresence
did pass through exits from above, we could well end up with quite "sticky" AnimatePresence
components where (for instance) route-level AnimatePresence
triggers exit animations on a nested (for example) tabbed interface. So the API actually become less composable.
@55Cancri have you got a reproduction of this?
@InventingWithMonster Yes, here is a codesandbox demonstrating the exact behavior I am observing:
@InventingWithMonster I can appreciate it seems unexpected to you. I think you might be a little biased though because you're building it and there's a consistency in the implementation.
Now - "what's expected" is something we can identify by just asking people. And even if you're right and it is unexpected by most people, it still is worth asking if some cohort of users does expect something different. So then it can just be seen as a matter of supporting both and the most expected behavior determining what the sensible default is.
I can only speak for myself. When I think about AnimatePresence - I see it as: I can write a component, give it enter & exit animation, and every time I render it - I will see that enter animation. Every time it's removed - I will see the exit animation. To get that result, I think the only way is to make AnimatePresence context-aware and delay when it is inside another AnimatePresence, and visa-versa on exit with the ancestor waiting for descendent AnimatePresence.
In isolated examples that only use one AnimatePresence in a sandbox, my mental model is compatible with the implementation. Only when AnimatePresence exists inside AnimatePresence does that break, and I have to really think hard about how AnimatePresence internally works to find a workaround to get the behavior I expected, and need to accomplish my goals.
It doesn't seem such a workaround is actually possible with AnimatePresence AFAICT - the only way to get multiple sequential enter and exit animations in a chain would be to use variants and show/hide things that are all rendered all the time but not visible. That approach doesn't work with a lot of other stuff that adds and removes components, and even just CSS layout in general, so it becomes a really leaky abstraction to design everything else around the animations.
In the end I find myself asking what the point of AnimatePresence is - if I can't really depend on it for showing and hiding things, and will in the end have to rethink my IA and refactor with variants that just make things visible or not with opacity or height but always present in the DOM. On the other hand if AnimatePresence did work the way I expect it would be incredibly powerful and would make so many complex animations much simpler.
Side note - if you search the github issues for "AnimatePresence", I'm not entirely sure but it seems like a lot of other issues people are describing are just variations of this root cause.
I just ran into the same issue as @55Cancri's reproduction link. I'm using react-router and my routes are inside AnimatePresence. Exit animations using AnimatePresence inside of a route component don't start.
@InventingWithMonster @joaopaulobdac Should I open this as a separate issue rather than keep it under this feature label? I have not been able to figure out a way to get the exit animations of grandchildren to animate. Do you see anything that I am doing wrong in my code @InventingWithMonster ?
@mattgperry Sorry but could you give us an update on this if possible? I try not to demand things of open source mantainers but I also haven't been able to fix this still
Just in case this helps anyone: I was having trouble with an AnimatePrescence
that was a child of another AnimatePrescence
component. I thought there might be a bug or limitation causing this but I just found out it's because my motion
elements didn't have "key" props.
Not the first time that one's caught me out.
Just revisted this. I simply don't agree orchestration is expected here, nor does it scale throughout a tree.
I can see an argument that exit
animations should fire in grandchildren, but this is also potentially not the case - think a list with 100s of items. It would make sense for one of them to animate out when removed from the list. But not at all reasonable to animate them all out when an ancestor is removed from its own AnimatePresence
.
If and when I get to this I think it's most reasonable to keep the current behaviour but also add a new prop, something like propagateExit
or the like, where you can opt-in to exits in parents propagating throughout children.
@mattgperry I am currently running into this "issue", and I would like to contribute to a solution. Do you have any suggestions or pointers on where I should start before I dive into the deep end?
For anyone else still looking at this solution: I was able to fix this issues, by rendering the child component having AnimatePresence, wrapped by a useMemo. So it updated the component inside on selection in my case, and did nothing on exiting together with the parent - which is exactly what I expected. Hope this helps someone :)
I'm currently running into the same issue here. Something like a propagateExit
would 100% solve my use case too 🙏.
2. Describe the bug
When AnimatePresence is used inside another AnimatePresence, all transitions happen simultaneously.
3. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/framer-motion-2-animating-shared-layouts-forked-jxxsc?file=/src/App.js
4. Steps to reproduce
Steps to reproduce the behavior:
5. Expected behavior
Note: The FAQ mentions "AnimatePresence" must be outside of the control conditions. I don't think this applies... because AnimatePresence can easily be aware of its own context and is itself fundamentally a way of overriding control conditions. The parent AnimatePresence should know that it contains another AnimatePresence and delay exiting. And the child AnimatePresence should know its parent has not finished animating in and delay its transition.
Sorry if this should be a "feature request" and not a "bug". It just seems like a bug to the user, but I can see how it's a special case to handle in the implementation. And AnimatePresence just isn't very useful if this isn't handled because otherwise it doesn't correlate to the mental model of conditional rendering in React. Components can render other components... and those components should be able to use AnimatePresence themselves independently without caring if they happened themselves to be rendered by another AnimatePresence.