I have been working on an SPA with React 18 and The Movie Database (TMDB) API.
I am now working on adding transitions between routes (pages) with the help of Framer Motion.
For this purpose, I have added a transition.js
file in /src
, with the following content:
import { motion } from "framer-motion";
const transition = (OgComponent) => {
return () => {
<>
<OgComponent />
<motion.div
className="slide-in"
initial={{ opacity: 0, x: 0 }}
animate={{ opacity: 1, x: 100, transition: { duration: 0.5 } }}
exit={{ opacity: 0, x: 0, transition: { duration: 0.5 } }}
/>
<motion.div />
</>
}
}
export default transition;
I import the above transition
in the app’s components with import transition from '../../transition'
and wrap the exported component in it. See the Movielist component as an example:
import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import axios from 'axios';
import Moviecard from '../Moviecard/Moviecard';
import transition from '../../transition';
function Movielist({ page_title, listMovies }) {
const API_URL = 'https://api.themoviedb.org/3';
const location = useLocation();
const [movies, setMovies] = useState([]);
const getMovies = async () => {
const { data: { results } } = await axios.get(`${API_URL}/movie/${listMovies}`, {
params: {
api_key: process.env.REACT_APP_API_KEY
}
});
setMovies(results);
}
const displayMovies = () => {
return movies.map(movie => (
<Moviecard
key={movie.id}
movie={movie}
/>
))
}
useEffect(() => {
getMovies();
}, [location])
return (
<>
<h1 className="page-title">{ page_title }</h1>
<div className="row movie-list">
{ displayMovies() }
</div>
</>
);
}
export default transition(Movielist);
In App.js I have "normal" routing:
import { Routes, Route } from 'react-router-dom';
import Topbar from './components/Topbar/Topbar';
import Footer from './components/Footer/Footer';
import Movielist from './components/Movielist/Movielist';
import Moviedetails from './components/Moviedetails/Moviedetails';
import Actordetails from './components/Actordetails/Actordetails';
function App() {
return (
<div className="App">
<Topbar />
<div className="container">
<Routes>
<Route path="/" element={<Movielist page_title="Now playing" listMovies="now_playing" />} />
<Route path="/top-rated" element={<Movielist page_title="Top rated" listMovies="top_rated" />} />
<Route path="/movie/:id" element={<Moviedetails />} />
<Route path="/actor/:id" element={<Actordetails />} />
</Routes>
</div>
<Footer />
</div>
);
}
export default App;
Sandbox
There is a sandbox with the code HERE.
The problem
Changing export default myComponent
to export default transition(myComponent)
makes the component fail to render.
It instead throws an error:
Request failed with status code 404 AxiosError: Request failed with
status code 404
at settle (http://localhost:3000/static/js/bundle.js:63343:12)
at XMLHttpRequest.onloadend (http://localhost:3000/static/js/bundle.js:62034:66)
Before trying to add smooth page transitions, it all worked fine.
Questions
- What am I doing wrong?
- What is the most reliable way to fix this issue?
2
Answers
The error occurs because the function returned from the
transition
function does not render a component; it returnsundefined
instead.To fix this, you can either remove the curly braces or define an explicit return for the inner function:
explicit return
The issue is with the arrow function in transition.js. The correct way to return the JSX is by using parentheses. Fix it like this:
This should resolve the issue and allow the component to render correctly.