React JS动态元素ID平滑滚动的代码优化方案
优化多组件平滑滚动,无需逐个声明ref
需求:点击Next/Prev按钮时实现平滑滚动效果,当前需要手动为每个组件声明ref,希望找到无需逐个声明ref的优化方法。
当前代码:
const aRef = useRef(null); const bRef = useRef(null); const cRef = useRef(null); const scrollTo = (id: any) => { console.log(id); scrollToComponent(id); }; const renderA = () => { return ( <div ref={aRef} className="step" id="A"> <h1>Component A</h1> <button className="next" onClick={() => scrollTo(bRef.current)}> NEXT </button> </div> ); }; const renderB = () => { return ( <div ref={bRef} className="step" id="B"> <h1>Component B</h1> <button className="next" onClick={() => scrollTo(cRef.current)}> NEXT </button> <button className="next" onClick={() => scrollTo(aRef.current)}> PREV </button> </div> ); }; return ( <div> {renderA()} {renderB()} {renderC()} </div> );
方法1:利用元素ID + scrollIntoView(最简洁)
完全不需要声明ref,直接借助元素的id属性,结合原生scrollIntoView的平滑配置实现滚动。
修改后的核心代码:
// 滚动函数:接收目标元素ID const scrollTo = (targetId: string) => { const targetEl = document.getElementById(targetId); targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; // 组件A const renderA = () => { return ( <div className="step" id="A"> <h1>Component A</h1> <button className="next" onClick={() => scrollTo('B')}> NEXT </button> </div> ); }; // 组件B const renderB = () => { return ( <div className="step" id="B"> <h1>Component B</h1> <button className="next" onClick={() => scrollTo('C')}> NEXT </button> <button className="prev" onClick={() => scrollTo('A')}> PREV </button> </div> ); };
优势:无需维护任何ref,利用原生DOM查询实现,代码极简,适合大多数场景。
方法2:用单个Ref统一管理所有组件
如果希望通过ref而非DOM查询管理元素,可以用一个对象类型的ref存储所有组件元素,避免逐个声明ref。
核心代码:
import { useRef } from 'react'; // 用对象ref存储所有组件元素 const stepRefs = useRef<Record<string, HTMLElement | null>>({}); const scrollTo = (targetId: string) => { const targetEl = stepRefs.current[targetId]; targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; const renderA = () => { return ( <div ref={el => stepRefs.current['A'] = el} className="step" id="A" > <h1>Component A</h1> <button className="next" onClick={() => scrollTo('B')}> NEXT </button> </div> ); }; const renderB = () => { return ( <div ref={el => stepRefs.current['B'] = el} className="step" id="B" > <h1>Component B</h1> <button className="next" onClick={() => scrollTo('C')}> NEXT </button> <button className="prev" onClick={() => scrollTo('A')}> PREV </button> </div> ); };
优势:所有组件元素统一管理,避免零散的ref变量,适合需要对组件元素做额外操作的场景。
方法3:动态生成组件(适合多组件场景)
如果组件数量较多,推荐把组件数据抽象成数组,循环渲染并自动绑定导航逻辑,彻底告别手动编写重复代码。
核心代码:
import { useRef } from 'react'; // 定义所有步骤的配置数据 const steps = [ { id: 'A', title: 'Component A', nextId: 'B', prevId: null }, { id: 'B', title: 'Component B', nextId: 'C', prevId: 'A' }, { id: 'C', title: 'Component C', nextId: null, prevId: 'B' }, ]; const stepRefs = useRef<Record<string, HTMLElement | null>>({}); const scrollTo = (targetId: string) => { const targetEl = stepRefs.current[targetId]; targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }; return ( <div> {steps.map(step => ( <div key={step.id} ref={el => stepRefs.current[step.id] = el} className="step" id={step.id} > <h1>{step.title}</h1> {step.nextId && ( <button className="next" onClick={() => scrollTo(step.nextId)}> NEXT </button> )} {step.prevId && ( <button className="prev" onClick={() => scrollTo(step.prevId)}> PREV </button> )} </div> ))} </div> );
优势:扩展性极强,新增/删除组件只需修改steps数组,无需改动渲染逻辑,代码复用率高。
内容的提问来源于stack exchange,提问作者Calvin Care
相关产品推荐
相关产品推荐

