背景
dors 的 modal 结合了 react-router-dom 使用:导航到某 url 时,modal 组件作为子组件通过 outlet 元素挂载到页面中,点击关闭 modal 的按钮时,导航返回上一级 url。这样便不用单独维护一个 modal 是否打开的状态。
问题场景
最近想为 modal 添加进入和退出时的动画,考虑使用 framer:在页面组件中使用 AnimationPresence 元素包裹 OutLet,将 modal 组件的 as 属性设置为 motion.div,再添加相应的动画属性,本应能够使 AnimationPresence 检测出变化modal 组件的挂载和卸载,添加相应动画。
调试中却遇到麻烦:退出时的动画不执行。
解决方法
最终在 Antonio Falcescu 在 Medium上的Animating React Pages with react-router-dom Outlet and framer-motion AnimatePresence 这篇博客中 中找到了解决方法:将 <Outlet> 元素改为手动使用 useOutlet hook 得到的 outlet 元素即可。
原因分析
此问题的原因是 AnimatePresence 只能感知到直接子元素的卸载或者其 key 的变化,来添加退出动画,而 Outlet 组件渲染为了OutletContext.Provider,来控制其子组件的子路由,再将真正的子元素塞进来,这样就多加了一层,导致 AnimatePresence,无法感知变化,从而无法添加退出动画
As Matt Perry (creator of framer-motion) mentions in an answer to a GitHub Issue with a similar problem to this, AnimatePresence is aware only of the direct children and can add exit animations only to those based on their unique keys.
The Outlet component uses the useOutlet hook to return a OutletContext.Provider which serves as the direct child of the Outlet component and is used to handle the routing of his own children (our pages).