skip to Main Content

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

  1. What am I doing wrong?
  2. What is the most reliable way to fix this issue?

2

Answers


  1. The error occurs because the function returned from the transition function does not render a component; it returns undefined instead.

    const transition = (OgComponent) => {
      // the function below does not return the component
      return () => {
        <>
          <OgComponent />
          ...
        </>
      }
    }
    

    To fix this, you can either remove the curly braces or define an explicit return for the inner function:

    explicit return

    const transition = (OgComponent) => {
      return () => {
        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 />
          </>
        )
    
      }
    }
    
    Login or Signup to reply.
  2. 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:

    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 } }}
          />
        </>
      );
    };
    
    export default transition;
    

    This should resolve the issue and allow the component to render correctly.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search