我正在尝试将视图滚动到特定组件。我正在使用 refs 来跟踪组件,并且尝试调用scrollIntoView 函数来移动到特定的引用。我遇到的问题是,当我尝试将对scrollIntoView 的调用置于useEffect 内部时,没有任何反应。该命令在鼠标单击时起作用,很可能是因为该组件已经渲染到屏幕上。
这是我的代码:
import React, { forwardRef, useEffect, useRef} from 'react';
import { Box, Button, Grid, Stack} from '@mui/material';
const component_1 = 'Component 1';
const component_2 = 'Component 2';
const component_3 = 'Component 3';
const component_4 = 'Component 4';
const component_5 = 'Component 5';
const component_6 = 'Component 6';
const component_7 = 'Component 7';
const component_8 = 'Component 8';
const component_9 = 'Component 9';
const component_10 = 'Component 10';
export const RefLabel = forwardRef((props, ref) => {
const { label } = props;
return <div ref={ref}>{label}</div>;
});
export const TheComponent = (props) => {
const sectionRefs = {
[component_1]: useRef(),
[component_2]: useRef()
[component_3]: useRef()
[component_4]: useRef()
[component_5]: useRef()
[component_6]: useRef()
[component_7]: useRef()
[component_8]: useRef()
[component_9]: useRef()
[component_10]: useRef()
}
const navToSection = (section) => {
sectionRefs[section].current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
//This does not work. The useEffect is called but the scrolling does not happen
navToSection(component_9);
}, []);
return (
<Grid container>
<Stack>
<Box>
<Button onClick={()=>{navToSection(component_9)}>Go to 9</Button>
<RefLabel ref=sectionRefs[component_1] label='label 1'>
<RefLabel ref=sectionRefs[component_2] label='label 2'>
<RefLabel ref=sectionRefs[component_3] label='label 3'>
<RefLabel ref=sectionRefs[component_4] label='label 4'>
<RefLabel ref=sectionRefs[component_5] label='label 5'>
<RefLabel ref=sectionRefs[component_6] label='label 6'>
<RefLabel ref=sectionRefs[component_7] label='label 7'>
<RefLabel ref=sectionRefs[component_8] label='label 8'>
<RefLabel ref=sectionRefs[component_9] label='label 9'>
<RefLabel ref=sectionRefs[component_10] label='label 10'>
</Box>
</Stack>
</Grid>
在此代码中,useEffect 根本不滚动。但是,当按下顶部按钮时,滚动视图会正确滚动到正确的参考标签。
还应该注意的是,此代码在浏览器滚动条内创建了一个滚动面板。无论如何,在组件完全渲染后从事件调用时,scrollIntoView 会起作用。我需要的是一种在渲染后自动滚动到组件的方法。在线文档建议将滚动逻辑放在 useEffect 中,但这对我不起作用。 (例如:https://www.codemzy.com/blog/react-scroll-to-element-on-render)
还要注意,这段代码是从真实代码中抽象出来的,这就是为什么它会做一些奇怪的事情,比如创建 10 个组件。真实的代码有一个内部滚动条,并且 RefLabels 包含在滚动窗格中。所有参考标签在滚动到可以显示的位置之前都是不可见的。
您没有在加载时滚动的原因有几个:
ref
的初始值为 null(除非您自己传递初始值),并且直到第一次渲染之后才会将其值更新到 DOM 对象useEffect
将在 before 第一次渲染之前运行,并且由于您指定了一个空的依赖项数组,所以这是它唯一运行的时间。如果您需要在渲染后立即访问元素,则一种常见模式是传递
ref
的回调而不是 ref 对象,并将元素存储在状态中。现在,您的 useEfect 可以依赖于状态片段,并在值更改后运行代码。您可能需要重新考虑一些逻辑才能将其适合您的用例,但这是我正在谈论的示例:
代码沙箱链接:https://codesandbox.io/p/sandbox/scroll-react-question-2t2hny
import { useState, useEffect } from "react";
export default function App() {
const [element, setElement] = useState(undefined);
useEffect(() => {
if (element) {
element.scrollIntoView();
}
}, [element]);
return (
<div>
<div style={{ height: "200vh" }}>
Big element to push the scroll element outside of view
</div>
<div ref={setElement}>Scroll element</div>
</div>
);
}
在上面的代码中,useEffect会运行两次。渲染之前一次(当元素状态仍未定义时。然后当元素状态更改为 DOM 对象时再一次。