AnimatePresence not working - trouble transitioning 2 components circularly

133 Views Asked by At

I am creating a login signup page in ReactJS using Framer Motion. I have created 2 components login and signup each having buttons, clicking on which the other component will render. I have used AnimatePresence to do exit and entry animation. but the exit animation is not working, in the exit part the component gets removed suddenly from the DOM tree without doing animation.

The component structure is like this:

  • UserLoginSignUp.jsx (Parent component)
    • Login. jsx** (children component)
    • SignUp.jsx** (children component)

UserLoginSignUp.jsx

import { Login } from "./Login";
import { Signup } from "./Signup";
import { AnimatePresence } from "framer-motion";

function UserLoginSignUp({ isloged, setisloged }) {
  return (
    <>
      <div>
        <div className="w-[40vw]">
          <AnimatePresence>
            {isloged ? (
              <Login setisloged={setisloged} />
            ) : (
              <Signup setisloged={setisloged} />
            )}
          </AnimatePresence>
        </div>
        <div></div>
      </div>
    </>
  );
}

export { UserLoginSignUp };

Login.jsx

import { useState } from "react";
import { motion } from "framer-motion";

function Login({ setisloged }) {
  const [masterKey, setMasterKey] = useState("");

  const handleLogin = () => {
  };

  const signupHandler = () => {
    setisloged(false);
  };

  return (
    <motion.div
      key="login"
      initial={{ opacity: 0, y: -100 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 1 }}
      exit={{ opacity: 0, y: 200 }}
      className="min-h-screen flex items-center justify-center bg-gradient-to-b from-[#95dab6] to-[#83b2d0]"
    >
      <motion.div
        initial={{ opacity: 0, y: -100 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 1 }}
        exit={{ opacity: 0, y: 200 }}
        className="bg-white p-8 rounded shadow-md w-96"
      >
        <h2 className="text-2xl font-semibold mb-4">Login</h2>
        <div className="mb-4">
          <input
            type="text"
            placeholder="Enter master key"
            className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-red-600  focus:border-blue-300"
            value={masterKey}
            onChange={(e) => setMasterKey(e.target.value)}
          />
        </div>
        <button
          className="w-full bg-[#95dab6] text-white py-2 rounded hover:bg-green-400"
          onClick={handleLogin}
        >
          Login
        </button>
        <p className="mt-4 text-center">
          Don't have an account?{" "}
          <span
            className="text-green-400 cursor-pointer"
            onClick={signupHandler}
          >
            Signup
          </span>
        </p>
      </motion.div>
    </motion.div>
  );
}

export { Login };

SignUp.jsx

import { useState } from "react";
import { motion } from "framer-motion";

function Signup({ setisloged }) {
  const [fullName, setFullName] = useState("");
  const [email, setEmail] = useState("");
  const [masterKey, setMasterKey] = useState("");

  const handleSignup = () => {
  };

  const loginHandler = () => {
    setisloged(true);
  };

  return (
    <motion.div
    key="signup"
    initial={{ opacity: 0, y: -100 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ duration: 1 }}
    exit={{ opacity: 0, y: 200 }}
      className="min-h-screen flex items-center justify-center  bg-gradient-to-b from-blue-600 to-purple-400">
      <motion.div
        initial={{ opacity: 0, y: -100 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 1 }}
        exit={{ opacity: 0, y: 200 }}
        className="bg-white p-8 rounded shadow-md w-96"
      >
        <h2 className="text-2xl font-semibold mb-4">Signup</h2>
        <div className="mb-4">
          <input
            type="text"
            placeholder="Enter Full Name"
            className="w-full px-3 py-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
            value={fullName}
            onChange={(e) => setFullName(e.target.value)}
          />
        </div>
        <div className="mb-4">
          <input
            type="email"
            placeholder="Enter Email"
            className="w-full px-3 py-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <div className="mb-4">
          <input
            type="text"
            placeholder="Enter your Masterkey"
            className="w-full px-3 py-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
            value={masterKey}
            onChange={(e) => setMasterKey(e.target.value)}
          />
        </div>
        <button
          className="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600 mb-2"
          onClick={handleSignup}
        >
          Signup
        </button>
        <button
          className="w-full bg-gray-300 text-gray-700 py-2 rounded hover:bg-gray-400"
          onClick={loginHandler}
        >
          Login
        </button>
      </motion.div>
    </motion.div>
  );
}

export { Signup };
1

There are 1 best solutions below

3
On

AnimatePresence has a mode prop which is sync by default. changing it to wait or popLayout may fix your issue

 <div className="w-[40vw]">
      <AnimatePresence mode="wait">
        {isloged ? (
          <Login key="login" setisloged={setisloged} />
        ) : (
          <Signup key="signup" setisloged={setisloged} />
        )}
      </AnimatePresence>
    </div>