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
相关产品推荐
相关产品推荐

