React多Carousel组件点击按钮仅底部轮播更新的问题求助
Alright, let's break down what's causing this issue and how to fix it quick.
The Root Problem
Right now, you're sharing one single state (currentIndex) and set of refs (listCarousel, maxScrollWidth) across all 5 carousels in your Genre component. When you loop through ShowList to render each carousel, every instance overwrites the same ref references, and all carousels rely on the same currentIndex value. That's why only the last carousel updates—its ref is the one that sticks, and all button clicks modify the same global index state.
The Fix: Isolate Each Carousel into Its Own Component
The solution is to wrap a single carousel's logic into a reusable component. This way, every carousel gets its own state, refs, and event handlers—no more cross-contamination.
Step 1: Create a standalone Carousel component
import { useState, useRef, useEffect } from "react"; export default function Carousel({ genreName, shows }) { // Each carousel gets its own state and refs const maxScrollWidth = useRef(0); const [currentIndex, setCurrentIndex] = useState(0); const listCarousel = useRef(null); const movePrev = () => { if (currentIndex > 0) { setCurrentIndex(prev => prev - 1); } }; const moveNext = () => { if (listCarousel.current && listCarousel.current.offsetWidth * currentIndex <= maxScrollWidth.current) { setCurrentIndex(prev => prev + 1); } }; const isDisabled = (direction) => { if (direction === "prev") { return currentIndex <= 0; } if (direction === "next" && listCarousel.current) { return listCarousel.current.offsetWidth * currentIndex >= maxScrollWidth.current; } return false; }; useEffect(() => { if (listCarousel.current) { listCarousel.current.scrollLeft = listCarousel.current.offsetWidth * currentIndex; } }, [currentIndex]); // Recalculate scroll width when shows data changes useEffect(() => { if (listCarousel.current) { maxScrollWidth.current = listCarousel.current.scrollWidth - listCarousel.current.offsetWidth; } }, [shows]); return ( <section> <div className="carousel my-12 mx-auto"> <h1 className="text-3xl leading-8 font-bold mb-12 text-slate-700"> {genreName} </h1> <div className="relative overflow-hidden"> <div className="flex justify-between absolute top left w-full h-full"> <button onClick={movePrev} className="hover:bg-blue-900/75 text-white w-10 h-full text-center opacity-75 hover:opacity-100 disabled:opacity-25 disabled:cursor-not-allowed z-10 p-0 m-0 transition-all ease-in-out duration-300" disabled={isDisabled("prev")} > <svg xmlns="http://www.w3.org/2000/svg" className="h-12 w-20 -ml-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} > <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" /> </svg> <span className="sr-only">Prev</span> </button> <button onClick={moveNext} className="hover:bg-blue-900/75 text-white w-10 h-full text-center opacity-75 hover:opacity-100 disabled:opacity-25 disabled:cursor-not-allowed z-10 p-0 m-0 transition-all ease-in-out duration-300" disabled={isDisabled("next")} > <svg xmlns="http://www.w3.org/2000/svg" className="h-12 w-20 -ml-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} > <path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" /> </svg> <span className="sr-only">Next</span> </button> </div> <div ref={listCarousel} className="carousel-container relative flex gap-1 overflow-hidden scroll-smooth snap-x snap-mandatory touch-pan-x z-0" > {shows.map((img, index) => ( <ul key={index}> <li className="carousel-item text-center relative w-64 h-64 snap-start" > <a href={img.link} className="h-full w-full aspect-square block bg-origin-padding bg-left-top bg-cover bg-no-repeat z-0" style={{ backgroundImage: `url(${img.image || ""})` }} > <img src={img.image || ""} alt={img.title} className="w-full rounded aspect-square hidden" /> </a> <a href={img.link} className="h-full w-full aspect-square block absolute top-0 left-0 transition-opacity duration-300 opacity-0 hover:opacity-100 bg-blue-800/75 z-10" > <h3 className="text-white py-6 px-3 mx-auto text-xl"> {img.title} </h3> </a> </li> </ul> ))} </div> </div> </div> </section> ); }
Step 2: Update your Genre component to use the new Carousel
import React from "react"; import ShowList from "./showlist.json"; import "./carousel.css"; import Carousel from "./Carousel"; export default function Genre() { return ( <div> {ShowList.map((genre, index) => ( <Carousel key={index} genreName={genre.name} shows={genre.Shows} /> ))} </div> ); }
Key Improvements
- Isolated State/Refs: Every carousel now has its own
currentIndexand refs, so button clicks only affect the carousel they belong to. - Fixed Dependency: The scroll width calculation now depends on the
showsprop, so it updates if your data changes. - Cleaner Code: Separating concerns makes your code easier to maintain and debug.
Bonus Optimization Tips
- Use a unique identifier (like an
idfrom your genre data) instead ofindexfor thekeyprop—this improves performance if you ever add/remove genres. - Add an
onErrorhandler to your images to show a placeholder if an image fails to load. - Consider using
requestAnimationFramein the scroll effect to make transitions even smoother.
内容的提问来源于stack exchange,提问作者EKivutha




