You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止React父组件重渲染,仅让react-glider子滑块随状态更新重渲染?

解决React-Glider点击外部滑块后重置位置的问题

问题根源

  1. Stale Closure隐患:handleClick的useCallback依赖数组为空,导致内部引用的selected始终是初始值0,状态更新逻辑失效;同时父组件因selected变化触发重渲染,带动Glider组件重新初始化,丢失当前滚动位置。
  2. 未缓存核心组件:父组件重渲染时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 00:50:13