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

React中scrollIntoView滚动到顶部后再次点击无效问题排查

问题描述
  • scrollIntoView功能基本正常,但存在异常场景:点击按钮(如John Doe)可滚动到对应区域,手动滚动到顶部后再次点击同一个已选中的按钮,无法再次触发滚动到目标区域,所有按钮均存在该问题。
  • 已在useEffect的依赖数组中加入selectedAdvisor状态变量,期望状态变化时触发scrollIntoView,但点击已选中元素时状态未更新,导致useEffect不执行。
  • 预期效果:即使滚动到顶部后再次点击已选中的元素,scrollIntoView仍能正常工作。
问题原因

点击已选中的按钮时,setSelectedAdvisor传入的是与当前selectedAdvisor相同的值,React会忽略这种无变化的状态更新,组件不会重新渲染,依赖selectedAdvisor的useEffect自然不会触发,也就不会执行scrollIntoView逻辑。

解决方案

方案一:修改状态结构,强制触发更新

给状态添加一个自增的标识,确保每次点击按钮都会触发状态更新,进而触发useEffect执行滚动:

import React, { useEffect, useState } from "react";

function AdvisorCard() {
  return <div className="advisorCard"></div>;
}
export const NewFilterAdvisor = () => {
  // 将状态改为包含名称和唯一标识的对象
  const [selectedAdvisor, setSelectedAdvisor] = useState({name: "Tony Stark", key: 0});

  useEffect(() => {
    let ele = document.getElementById(`${selectedAdvisor.name}`);
    if (ele) {
      ele.scrollIntoView({
        behavior: "smooth",
        block: "center",
      });
    }
  }, [selectedAdvisor]);

  const handleSelectAdvisor = (name) => {
    setSelectedAdvisor(prev => ({
      name,
      key: prev.key + 1 // 每次点击更新唯一标识,强制触发状态变化
    }));
  };

  return (
    <div className="newFilterAdvisorContainer">
      <div className="advisorsBtnContainer">
        <button
          onClick={() => handleSelectAdvisor("Tony Stark")}
          className="advisorBtn"
        >
          Tony Stark
        </button>
        <button
          onClick={() => handleSelectAdvisor("Steve Rogers")}
          className="advisorBtn"
        >
          Steve Rogers
        </button>
        <button
          onClick={() => handleSelectAdvisor("Bruce Banner")}
          className="advisorBtn"
        >
          Bruce Banner
        </button>
        <button
          onClick={() => handleSelectAdvisor("Thor")}
          className="advisorBtn"
        >
          Thor
        </button>
        <button
          onClick={() => handleSelectAdvisor("John Doe")}
          className="advisorBtn"
        >
          John Doe
        </button>
      </div>

      <div id="Tony Stark" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Tony's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="Steve Rogers" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Steve's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="Bruce Banner" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Bruce's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="Thor" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Thor's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="John Doe" className="advisorSection">
        <h1 className="advisorCardContainerHeading">John's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>
    </div>
  );
};

方案二:直接在点击事件中执行滚动逻辑

去掉useEffect的依赖逻辑,直接在按钮点击时查找目标元素并执行滚动,这样无论是否为当前选中元素,点击就触发滚动:

import React, { useState } from "react";

function AdvisorCard() {
  return <div className="advisorCard"></div>;
}
export const NewFilterAdvisor = () => {
  const [selectedAdvisor, setSelectedAdvisor] = useState("Tony Stark");

  const scrollToAdvisor = (name) => {
    setSelectedAdvisor(name); // 仍保留状态更新用于其他逻辑(如高亮按钮)
    let ele = document.getElementById(`${name}`);
    if (ele) {
      ele.scrollIntoView({
        behavior: "smooth",
        block: "center",
      });
    }
  };

  return (
    <div className="newFilterAdvisorContainer">
      <div className="advisorsBtnContainer">
        <button
          onClick={() => scrollToAdvisor("Tony Stark")}
          className="advisorBtn"
        >
          Tony Stark
        </button>
        <button
          onClick={() => scrollToAdvisor("Steve Rogers")}
          className="advisorBtn"
        >
          Steve Rogers
        </button>
        <button
          onClick={() => scrollToAdvisor("Bruce Banner")}
          className="advisorBtn"
        >
          Bruce Banner
        </button>
        <button
          onClick={() => scrollToAdvisor("Thor")}
          className="advisorBtn"
        >
          Thor
        </button>
        <button
          onClick={() => scrollToAdvisor("John Doe")}
          className="advisorBtn"
        >
          John Doe
        </button>
      </div>

      <div id="Tony Stark" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Tony's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="Steve Rogers" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Steve's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="Bruce Banner" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Bruce's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="Thor" className="advisorSection">
        <h1 className="advisorCardContainerHeading">Thor's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>

      <div id="John Doe" className="advisorSection">
        <h1 className="advisorCardContainerHeading">John's Advisors</h1>
        <div className="advisorCardContainer">
          <AdvisorCard />
          <AdvisorCard />
          <AdvisorCard />
        </div>
      </div>
    </div>
  );
};

内容的提问来源于stack exchange,提问作者Sachin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:36:05