如何修复使用成帧器运动 useAnimate hook 时无法给功能组件提供引用的问题?

问题描述 投票:0回答:1

on the left side close icons is animated when clicked after that the dropdown nav comein however i get error i.e on the left side

导航栏组件代码

import Humburger from "./ui/Humburger";
const navlins = ["HOME", "PAGES", "PRODUCTS", "ARTICLES", "CONTACT"];
const Navbar = () => {
  const [isNavbarActive, setIsNavbarActive] = useState(false);
  const toogleNavbar = () => setIsNavbarActive(!isNavbarActive);
  return (
    <>
      <div className="relative bg-white h-[4.5rem] z-20">
        <div className="w-full h-full flex justify-between items-center px-4">
          <Image
            width={120}
            height={70}
            src="https://cdn.prod.website-files.com/65478d390c8996a757a4faaa/65c4aacccb9a41d80d0d66f7_Stride-logo-dark.svg"
            alt="nav-logo"
          />
          <div className="hidden">
            <ul className="flex w-full border border-green-500">
              <li>Home</li>
              <li>Pages</li>
              <li>Product</li>
              <li>Article</li>
              <li>contact</li>
            </ul>
          </div>

          <div className="flex items-center gap-4">
            <div className="relative h-10 w-10 p-2">
              <Image
                className="z-0 inset-0"
                src="https://cdn.prod.website-files.com/65478d390c8996a757a4faaa/659e5cb3e032b694713f63c0_Add%20to%20basket.svg"
                width={20}
                height={20}
                alt="cart-icon"
              />
              <div className="z-20 absolute shadow-xl top-1 -right-[2px] bg-gray-200 text-black rounded-full text-[0.6em] h-[1.1rem] w-[1.1rem] grid place-content-center font-bold">
                5
              </div>
            </div>

            <button className="border flex items-center justify-center border-slate-400 hover:focus-within:none bg-black text-white rounded-full px-4 py-1.5 hover:bg-white transition-all ease-in hover:text-black">
              Login
            </button>

            <Humburger onClick={() => setIsNavbarActive(!isNavbarActive)} />
          </div>
        </div>
      </div>

      <AnimatePresence mode="popLayout">
        {isNavbarActive && <NavbarDropdown />}
      </AnimatePresence>
    </>
  );
};

export default Navbar;

const NavbarDropdown = () => {
  const dropdownVariants = {
    initial: {
      opacity: 0,
      height: 0,
    },
    enter: {
      opacity: 1,
      height: "14rem",
      transition: {
        ease: [0.83, 0, 0.17, 1],
        duration: 0.5,
        staggerChildren: 0.1,
        staggerDirection: 1,
        delayChildren: 0.2,
        // Stagger the li children after the dropdown opens
      },
    },
    exit: {
      height: 0,
      transition: {
        ease: [0.83, 0, 0.17, 1],
        staggerChildren: 0.1, // Stagger the li children on exit as well
        staggerDirection: -1,
        delay: 0.3,
        // Reverse the direction (exit from bottom to top)
        // Delay before shrinking the dropdown after the li items
      },
    },
  };

  const liVariants = {
    initial: {
      opacity: 0,
      x: -50,
    },

    enter: {
      opacity: 1,
      x: 0,
      transition: {
        ease: [0.22, 1, 0.36, 1],
        duration: 0.5,
      },
    },

    exit: {
      opacity: 0,
      x: -50,
      transition: {
        ease: [0.22, 1, 0.36, 1],
      },
    },
  };

  return (
    <motion.div
      variants={dropdownVariants}
      initial="initial"
      animate="enter"
      exit="exit"
      className="absolute origin-top top-[4.6rem] w-full bg-white drop-shadow-lg"
    >
      <motion.ul className="space-y-5 px-3">
        {navlins.map((navlin, index) => (
          <motion.li
            custom={index}
            key={index}
            variants={liVariants} // Apply variants to each li item
            className="text-gray-600 text-[1em] font-normal flex items-center gap-5"
          >
            {navlin}
            {index === 0 || index === 1 ? (
              <IoMdArrowDown size={24} color="gray" />
            ) : (
              <></>
            )}
          </motion.li>
        ))}
      </motion.ul>
    </motion.div>
  );
};

Humburger 组件代码

"use client";
import { useAnimate } from "framer-motion";
import React, { forwardRef, useState } from "react";

const Humburger = forwardRef<HTMLDivElement, { onClick: () => void }>(
  ({ onClick }, forwardedRef) => {
    const [scope, animate] = useAnimate();
    const [isHumburgOpen, setIsHumburgOpen] = useState(false);
    const [isAnimating, setIsAnimating] = useState(false);

    const animateHumburger = async () => {
      if (isAnimating) return null;

      setIsAnimating(!isAnimating);
      onClick();
      if (!isHumburgOpen) {
        animate(
          "#top",
          {
            y: "0.45rem",
          },
          {
            ease: [0.36, 0, 0.66, -0.56],
            duration: 0.5,
          }
        );
        await animate(
          "#bottom",
          {
            y: `-${0.45}rem`,
            opacity: 0,
          },
          {
            ease: [0.36, 0, 0.66, -0.56],
            duration: 0.5,
          }
        );
        animate(
          "#top",
          {
            rotate: "136deg",
          },
          {
            ease: [0.22, 1, 0.36, 1],
            duration: 0.5,
          }
        );
        animate(
          "#middle",
          {
            rotate: "43deg",
          },
          {
            ease: [0.22, 1, 0.36, 1],
            duration: 0.5,
          }
        );
        setIsHumburgOpen(!isHumburgOpen);
      } else {
        animate(
          "#top",
          {
            rotate: "0deg",
          },
          {
            ease: [0.36, 0, 0.66, -0.56],
            duration: 0.5,
          }
        );
        await animate(
          "#middle",
          {
            rotate: "0deg",
          },
          {
            ease: [0.36, 0, 0.66, -0.56],

            duration: 0.5,
          }
        );
        animate(
          "#top",
          {
            y: "0rem",
          },
          {
            ease: [0.22, 1, 0.36, 1],
            duration: 0.5,
          }
        );
        await animate(
          "#bottom",
          {
            y: 0,
            opacity: 1,
          },
          {
            ease: [0.22, 1, 0.36, 1],
            duration: 0.5,
          }
        );

        setIsHumburgOpen(!isHumburgOpen);
      }
      setIsAnimating(false);
    };
    return (
      <div ref={scope}>
        <a
          onClick={animateHumburger}
          className="hover:focus-within:outline-none hover:focus:outline-none flex flex-col items-end justify-center h-24 gap-1 mt-50 cursor-pointer"
        >
          <span
            id="top"
            className="origin-center h-[3px] w-6 bg-black  block rounded-full"
          ></span>
          <span
            id="middle"
            className="h-[3px] w-6  bg-black   rounded-full"
          ></span>
          <span
            id="bottom"
            className="h-[3px] w-4 bg-black  rounded-full"
          ></span>
        </a>
      </div>
    );
  }
);

export default Humburger;

函数 animateHumburger 只是将三个垂直条动画化为十字。 我正在使用framermotion来制作动画,并且正在使用useAnimate钩子,我必须将范围传递给主容器并将id传递给子元素,通过它我将在useanimatehook中使用它们来制作动画。 这个 humburger 组件完成后,我在 navbar 组件中使用了它,但出现错误。但是 ref 正在 humburger 组件本身中使用,为什么我需要按照 chatgpt 的建议传递 useRef 钩子,尽管它也没有解决问题。 无法给函数组件提供 refs 它实际上意味着什么? 即使在反应文档中,他们也已在功能组件中使用,或者我弄错了。请纠正我并帮助我解决问题??

this image is from react official website where they its clearly show using use Ref in functional component or am i getting it wrong please correct me..

还给我解决如何修复此错误的问题

reactjs next.js framer-motion react-ref
1个回答
0
投票

问题在于

NavbarDropdown
没有包裹在
forwardRef
中。

文档状态

自定义组件注意:使用 popLayout 模式时,作为自定义组件的 AnimatePresence 的任何直接子组件都必须包装在 React 的forwardRef 函数中,将提供的引用转发到您希望从布局中弹出的 DOM 节点。

您正在使用自定义组件 (

NavbarDropdown
) 作为
AnimatePresence
的直接子组件,并且您还使用
popLayout
模式:

<AnimatePresence mode="popLayout">
    {isNavbarActive && <NavbarDropdown />}
</AnimatePresence>

所以这个限制适用于你。

要修复,请用

NavbarDropdown
包裹
forwardRef
并将该引用应用于要“弹出”的元素。我在这里假设它是
NavbarDropdown
中的第一个元素。

const NavbarDropdown = forwardRef<HTMLDivElement, {}>((props, ref) => {
  const dropdownVariants = {
    initial: {
      opacity: 0,
      height: 0,
    },
    enter: {
      opacity: 1,
      height: "14rem",
      transition: {
        ease: [0.83, 0, 0.17, 1],
        duration: 0.5,
        staggerChildren: 0.1,
        staggerDirection: 1,
        delayChildren: 0.2,
        // Stagger the li children after the dropdown opens
      },
    },
    exit: {
      height: 0,
      transition: {
        ease: [0.83, 0, 0.17, 1],
        staggerChildren: 0.1, // Stagger the li children on exit as well
        staggerDirection: -1,
        delay: 0.3,
        // Reverse the direction (exit from bottom to top)
        // Delay before shrinking the dropdown after the li items
      },
    },
  };

  const liVariants = {
    initial: {
      opacity: 0,
      x: -50,
    },

    enter: {
      opacity: 1,
      x: 0,
      transition: {
        ease: [0.22, 1, 0.36, 1],
        duration: 0.5,
      },
    },

    exit: {
      opacity: 0,
      x: -50,
      transition: {
        ease: [0.22, 1, 0.36, 1],
      },
    },
  };

  return (
    <motion.div
      ref={ref}
      variants={dropdownVariants}
      initial="initial"
      animate="enter"
      exit="exit"
      className="absolute origin-top top-[4.6rem] w-full bg-white drop-shadow-lg"
    >
      <motion.ul className="space-y-5 px-3">
        {navlins.map((navlin, index) => (
          <motion.li
            custom={index}
            key={index}
            variants={liVariants} // Apply variants to each li item
            className="text-gray-600 text-[1em] font-normal flex items-center gap-5"
          >
            {navlin}
            {index === 0 || index === 1 ? (
              <IoMdArrowDown size={24} color="gray" />
            ) : (
              <></>
            )}
          </motion.li>
        ))}
      </motion.ul>
    </motion.div>
  );
});
© www.soinside.com 2019 - 2024. All rights reserved.