Rahul Sahani
Rahul Sahani

Reputation: 1

How to Properly Use React.memo with Framer Motion and Next.js Image Component

I'm working on a Next.js project where I need to display hero images using the Image component from next/image and animate them with framer-motion. I also want to optimize performance by using React.memo However, I'm encountering issues with the images not rendering correctly when the hero prop is null. and all of them getting animated when only one is updated.

Here is the relevant code:

import { motion } from "framer-motion";
import Image from "next/image";
import React from "react";

interface Hero {
  name: string;
  img: string;
}

const HeroImage = React.memo(
  ({ hero, index }: { hero: Hero | null; index: Number }) => {
    return (
      <motion.div
        key={"hero" + index}
        className="relative w-full h-full"
        layoutId={"hero" + index}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
      >
        <Image
          key={"hero" + index}
          src={hero?.img || ""}
          alt={hero?.name || "Placeholder"}
          layout="fill"
          objectFit="cover"
          draggable={false}
          style={{
            visibility: hero ? "visible" : "hidden",
          }}
        />
      </motion.div>
    );
  }
);

const HeroCard = React.memo(
  ({ hero, index }: { hero: Hero | null; index: Number }) => {
    return (
      <div
        className="relative overflow-hidden rounded flex-grow bg-cover"
        style={{
          backgroundImage: `url('/placeholder.png')`,
        }}
      >
        <HeroImage hero={hero} index={index} />
        <div className="absolute bottom-0 left-0 right-0 bg-black/70 text-white text-center py-1">
          {hero?.name}
        </div>
      </div>
    );
  }
);

export { HeroImage, HeroCard };

Any help or suggestions would be greatly appreciated!

I wanted a slide up animation when a hero is picked but when i pick one it works fine but if i select another all the previous ones also gets animated. i only want the animation on the one which is updated

Upvotes: 0

Views: 85

Answers (1)

Raja Jahanzaib
Raja Jahanzaib

Reputation: 640

You can wrap motion.div in AnimatePresence so it only update hero image which is currently rendered or updated. Apply initial,animate and exit to each image as it mounts or unmounts.

<AnimatePresence>
    {hero && (
      <motion.div
        key={hero.name + index} 
        className="relative w-full h-full"
        layoutId={"hero" + index}
        initial={{ y: 50, opacity: 0 }} 
        animate={{ y: 0, opacity: 1 }}
        exit={{ y: 50, opacity: 0 }}
        transition={{ duration: 0.5 }}
      >
        <Image
          src={hero.img}
          alt={hero.name}
          layout="fill"
          objectFit="cover"
          draggable={false}
        />
      </motion.div>
    )}
  </AnimatePresence>
);

}

Upvotes: 0

Related Questions