結論
使っている`Link` コンポーネントが間違っていました。
material-uiの`Link` ではなく
react-router-domの`Link` を使用することで解決しました。
FramerMotionの「exit」だけアニメーションが効かない場合はぜひreact-router-domのLinkを使うことを試してみていただければと思います。
表示側ではなく遷移するための書き方が問題だった
表示側の書き方が何か違うのだろう、と思って表示側をずっと調整していましたが一向に直らず。
調査中ふと、`Link href` じゃなくて`Link to` を使っているなというところが気になり確認したところ間違いに気づいたという流れでした。
material-uiのLinkは飛び先をhrefで指定しているのに対し、react-router-domのLinkはtoで指定する必要があります。
表示側の書き方を疑ってかなりの時間を使ってしまいましたが、解決して本当に良かったです。
まあ全体的にあまり良くない書き方をしているところもあったので、ゆっくりリファクタリングできたという意味では無駄にはならなかったのでよかったのですが久しぶりに大きな行き詰まりでした。
成功例として以下にソースを載せておきます。
withRoot.tsx
export default function withRoot<P extends JSX.IntrinsicAttributes>(
Component: React.ComponentType<P>
) {
function WithRoot(props: P) {
return (
<motion.div
initial={{ opacity: 0, x: 0 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 0 }}
transition={{
duration: 1,
}}
>
<Component {...props} />
</motion.div>
);
}
return WithRoot;
}
Home.tsx
function Home() {
return (
<React.Fragment>
<Link to="sign-in">ログイン画面へ</Link>
<Link to="my-page">マイページへ</Link>
</React.Fragment>
);
}
export default withRoot(Home);
App.tsx
function App() {
const location = useLocation();
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<AppBar />
<AnimatePresence exitBeforeEnter>
<Routes location={location} key={location.pathname}>
<Route path="/" element={<Home />} />
<Route path="sign-in" element={<SignIn />} />
<Route path="my-page" element={<MyPage />} />
</Routes>
</AnimatePresence>
<AppFooter />
</ThemeProvider>
);
};
export default App;
index.tsx
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
まとめ
ReactRouterDOM6では前バージョンからいくつか記述方法が変更されたとのことで、その辺りの書きミスかなと思っていました。
Router -> BrowserRouter
Switch -> Routes
など。遷移方法の間違いだったとは。。しかしもう似たようなところでは詰まらないと思うので若干レベルアップできた気がします。
以上です。
Next.jsとのことですが、全く同じ症状の方がいらっしゃいました。↓