如何阻止React父组件重渲染,仅让react-glider子滑块随状态更新重渲染?
解决React-Glider点击外部滑块后重置位置的问题
问题根源
- Stale Closure隐患:
handleClick的useCallback依赖数组为空,导致内部引用的selected始终是初始值0,状态更新逻辑失效;同时父组件因selected变化触发重渲染,带动Glider组件重新初始化,丢失当前滚动位置。 - 未缓存核心组件:父组件重渲染时,
Glider作为子组件会被重新渲染,直接重置滑块到初始显示位置。
解决方案
步骤1:修复useCallback依赖,解决Stale Closure
将selected加入useCallback的依赖数组,确保回调函数能获取最新状态值,同时让回调引用仅在必要时更新:
const handleClick = useCallback((id) => { if (id !== selected) { setSelected(id); } }, [selected]); // 添加selected到依赖数组
步骤2:封装缓存Glider组件,避免不必要重渲染
把Glider及其子元素封装成独立的Memoized组件,仅在传入的slides、selected或handleClick变化时才重渲染:
const MemoizedGlider = React.memo(({ slides, selected, handleClick, gliderRef }) => { return ( <Glider ref={gliderRef} slidesToShow="auto" slidesToScroll="auto" draggable hasDots scrollLock > {slides.map((category) => ( <MemoizedCategory handleClick={handleClick} key={category.index} selected={selected === category.index} title={category.title} index={category.index} /> ))} </Glider> ); });
步骤3:用Glider实例控制滚动(可选,确保点击滑块可见)
通过ref获取Glider实例,点击时调用滚动方法,让选中的滑块自动进入视野:
export default function App() { const [selected, setSelected] = React.useState(0); const gliderRef = React.useRef(null); const handleClick = useCallback((id) => { if (id !== selected) { setSelected(id); const slideIndex = slides.findIndex(s => s.index === id); if (gliderRef.current) { gliderRef.current.scrollItem(slideIndex); } } }, [selected]); return ( <div className="w-[100%]"> <MemoizedGlider slides={slides} selected={selected} handleClick={handleClick} gliderRef={gliderRef} /> </div> ); }
完整优化代码
import "./styles.css"; import "glider-js/glider.min.css"; import React, { useCallback, memo } from "react"; import Glider from "react-glider"; const slides = [ { index: 1, title: "Slide 1" }, { index: 2, title: "Slide 2" }, { index: 3, title: "Slide 3" }, { index: 4, title: "Slide 4" }, { index: 5, title: "Slide 5" }, { index: 6, title: "Slide 6" }, { index: 7, title: "Slide 7" }, { index: 8, title: "Slide 8" }, { index: 9, title: "Slide 9" }, { index: 10, title: "Slide 10" } ]; const MemoizedCategory = memo(function Category({ title, selected, handleClick, index }) { console.log("Re Rendered ", index); return ( <div onClick={() => handleClick(index)} className={`card ${selected ? `bg-blueviolet` : `bg-black`} `} > <h1>{title}</h1> </div> ); }); const MemoizedGlider = memo(({ slides, selected, handleClick, gliderRef }) => { return ( <Glider ref={gliderRef} slidesToShow="auto" slidesToScroll="auto" draggable hasDots scrollLock > {slides.map((category) => ( <MemoizedCategory handleClick={handleClick} key={category.index} selected={selected === category.index} title={category.title} index={category.index} /> ))} </Glider> ); }); export default function App() { const [selected, setSelected] = React.useState(0); const gliderRef = React.useRef(null); const handleClick = useCallback((id) => { if (id !== selected) { setSelected(id); const slideIndex = slides.findIndex(s => s.index === id); if (gliderRef.current) { gliderRef.current.scrollItem(slideIndex); } } }, [selected]); console.log("Parent Re Rendered"); return ( <div className="w-[100%]"> <MemoizedGlider slides={slides} selected={selected} handleClick={handleClick} gliderRef={gliderRef} /> </div> ); }
.App { font-family: sans-serif; text-align: center; } .card { display: flex; height: 100px !important; width: 200px !important; background: black; color: white; margin: 2px; justify-content: center; align-items: center; } .bg-blueviolet { background-color: blueviolet; } .bg-black { background-color: black; }
说明
- 修复依赖后,回调函数能正确响应最新状态,同时减少不必要的子组件重渲染。
- 缓存
MemoizedGlider后,父组件状态变化时不会触发Glider重置,保留当前滚动位置。 - 新增的滚动控制逻辑,确保点击的滑块自动进入视野,优化用户体验。
内容的提问来源于stack exchange,提问作者trying_it
相关产品推荐
相关产品推荐

