Как правильно использовать React.memo с Framer Motion и компонентом изображения Next.js

Вопрос или проблема

Я работаю над проектом на Next.js, где мне нужно отображать изображения героев, используя компонент Image из next/image и анимировать их с помощью framer-motion. Я также хочу оптимизировать производительность, используя React.memo. Однако я сталкиваюсь с проблемами, когда изображения не отображаются корректно, если проп hero равен null, и все они анимируются, когда обновляется только одно из них.

Вот соответствующий код:

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 };

Любая помощь или предложения будут очень полезны!

Я хотел анимацию с выдвижением, когда выбирается герой, но когда я выбираю одного, все работает нормально, но если я выбираю другого, все предыдущие также анимируются. Я хочу анимацию только для того, который обновляется.

Ответ или решение

Как правильно использовать React.memo с Framer Motion и компонентом Image из Next.js

При разработке на Next.js с использованием фреймворков, таких как Framer Motion, важно оптимизировать рендеринг компонентов, особенно при работе с изображениями. Данная статья поможет вам правильно интегрировать React.memo, Framer Motion и компонент Image из next/image, чтобы избежать проблем с рендерингом и анимацией.

Проблема с эффектом анимации

Вы упомянули о том, что при изменении состояния анимации все компоненты HeroImage анимируются, даже если изменился только один из них. Это связано с тем, что по умолчанию, если ключи и свойства компонента остаются неизменными, React обновляет их, что приводит к "переанимации" для всех остальных компонентов.

Чтобы избежать этого поведения, следуйте следующим шагам:

1. Обеспечение ключей для анимации

Убедитесь, что у каждого компонента есть уникальный ключ, который будет отслеживать его изменения. Хотя вы уже добавили ключ к motion.div, это не совсем предотвратит повторную анимацию. Убедитесь, что ключи действительно уникальны и привязаны к изменению состояния hero.

<motion.div
  key={hero ? hero.img : "placeholder" + index}
  ...
>

Это изменение гарантирует, что компонент будет обновлен только в случае изменения состояния hero.

2. Оптимизация использования React.memo

Если hero может быть null, вы можете оптимизировать условия рендеринга следующим образом:

const HeroImage = React.memo(
  ({ hero, index }: { hero: Hero | null; index: number }) => {
    if (!hero) {
      return (
        <motion.div key={"placeholder" + index} className="relative w-full h-full">
          <Image
            src="/placeholder.png"
            alt="Placeholder"
            layout="fill"
            objectFit="cover"
          />
        </motion.div>
      );
    }

    return (
      <motion.div
        key={"hero" + index}
        layoutId={"hero" + index}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
      >
        <Image
          src={hero.img}
          alt={hero.name}
          layout="fill"
          objectFit="cover"
          draggable={false}
        />
      </motion.div>
    );
  },
  (prevProps, nextProps) => prevProps.hero === nextProps.hero
);

Используя функцию сравнения в React.memo, вы можете предотвратить повторный рендеринг, когда свойства не изменяются.

3. Обработка анимации только для изменённых элементов

При изменении состояния выберите только те компоненты, которые изменились, и пересоздайте их анимацию. Это можно сделать, изменяя состояние в основном компоненте, который управляет списком HeroCard. Например, если вы используете useState для хранения текущего героя, рендерите только тот компонент, который обновился.

const [selectedHero, setSelectedHero] = useState<Hero | null>(null);

const handleHeroSelect = (hero: Hero) => {
  setSelectedHero(hero);
  // Для вызова анимации можно использовать состояние анимации
};

Заключение

Следуя этим рекомендациям, вы сможете оптимизировать использование React.memo, Framer Motion и компонента Image из next/image, а также устранить проблемы с анимацией и рендерингом. Это не только улучшит производительность вашего приложения, но и создаст более плавный пользовательский опыт.

Если у вас есть дополнительные вопросы или вам требуется помощь в конкретных аспектах реализации, не hesitate to ask!

Оцените материал
Добавить комментарий

Капча загружается...