R. *_*sch 20 javascript ref reactjs js-scrollintoview use-effect
我现在正在用 React 构建一个旋转木马。要滚动到我使用的单个幻灯片,document.querySelector
如下所示:
useEffect(() => {
document.querySelector(`#slide-${activeSlide}`).scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}, [activeSlide]);
Run Code Online (Sandbox Code Playgroud)
这是不好的做法吗?毕竟,我在这里直接访问 DOM?这样做的 React 方式是什么?
编辑:完整return
方法
return (
<>
<button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
<Wrapper id="test">
{children.map((child, i) => {
return (
<Slide id={`slide-${i}`} key={`slide-${i}`}>
{child}
</Slide>
);
})}
</Wrapper>
<button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
</>
);
Run Code Online (Sandbox Code Playgroud)
T.J*_*der 26
我无法回答是否为此使用 refs 的“您应该”部分,除非您这样做,id
除非您将它们用于其他用途,否则您不需要这些值。
但这是你的方式:
使用useRef(null)
创建裁判。
const activeSlideRef = useRef(null);
Run Code Online (Sandbox Code Playgroud)把它放在Slide
当前活动的
<Slide ref={i === activeSlide ? activeSlideRef : null} ...>
Run Code Online (Sandbox Code Playgroud)在您的 中useEffect
,使用 ref 的current
属性
useEffect(() => {
if (activeSlideRef.current) {
activeSlideRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}
}, [activeSlide]);
Run Code Online (Sandbox Code Playgroud)
(我认为这activeSlide
是该效果的合理依赖。您不能使用 ref,ref 本身不会改变......)
现场示例,div
为了方便起见,我已将您的一些组件转换为s:
const activeSlideRef = useRef(null);
Run Code Online (Sandbox Code Playgroud)
<Slide ref={i === activeSlide ? activeSlideRef : null} ...>
Run Code Online (Sandbox Code Playgroud)
useEffect(() => {
if (activeSlideRef.current) {
activeSlideRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}
}, [activeSlide]);
Run Code Online (Sandbox Code Playgroud)
在您问过的评论中:
你知道是否可以
useEffect
在第一次渲染时禁用这里?
为了保留每个组件的非状态信息,有趣的是您使用useRef
. 在对文档useRef
指出,这不只是对DOM元素的引用,它也是每个组件的非国有的数据。所以你可以有
const firstRenderRef = useRef(true);
Run Code Online (Sandbox Code Playgroud)
然后在您的useEffect
回调中,检查firstRenderRef.current
&mndash; 如果是true
,设置它false
,否则进行滚动:
const {useEffect, useRef, useState} = React;
function Deck({children}) {
const [activeSlide, setActiveSlide] = useState(0);
const activeSlideRef = useRef(null);
useEffect(() => {
if (activeSlideRef.current) {
activeSlideRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}
}, [activeSlide]);
const moveLeft = Math.max(0, activeSlide - 1);
const moveRight = Math.min(children.length - 1, activeSlide + 1);
return (
<React.Fragment>
<button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
<div id="test">
{children.map((child, i) => {
const active = i === activeSlide;
return (
<div className={`slide ${active ? "active" : ""}`} ref={active ? activeSlideRef : null} id={`slide-${i}`} key={`slide-${i}`}>
{child}
</div>
);
})}
</div>
<button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
</React.Fragment>
);
}
ReactDOM.render(
<Deck>
<div>slide 0 </div>
<div>slide 1 </div>
<div>slide 2 </div>
<div>slide 3 </div>
<div>slide 4 </div>
<div>slide 5 </div>
<div>slide 6 </div>
<div>slide 7 </div>
<div>slide 8 </div>
<div>slide 9 </div>
</Deck>,
document.getElementById("root")
);
Run Code Online (Sandbox Code Playgroud)
.slide {
height: 4em;
vertical-align: middle;
text-align: center;
}
#test {
overflow: scroll;
max-height: 20em;
}
.active {
font-weight: bold;
color: blue;
}
Run Code Online (Sandbox Code Playgroud)
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
Run Code Online (Sandbox Code Playgroud)
作为一个思想实验,我写了一个钩子来简化人体工程学:
function useInstance(instance = {}) {
// assertion: instance && typeof instance === "object"
const ref = useRef(instance);
return ref.current;
}
Run Code Online (Sandbox Code Playgroud)
用法:
const inst = useInstance({first: true});
Run Code Online (Sandbox Code Playgroud)
在 中useEffect
,如果inst.first
为真,则执行inst.first = false;
;否则,进行滚动。
居住:
const firstRenderRef = useRef(true);
Run Code Online (Sandbox Code Playgroud)
const {useEffect, useRef, useState} = React;
function Deck({children}) {
const [activeSlide, setActiveSlide] = useState(0);
const activeSlideRef = useRef(null);
// *** Use a ref with the initial value `true`
const firstRenderRef = useRef(true);
console.log("render");
useEffect(() => {
// *** After render, don't do anything, just remember we've seen the render
if (firstRenderRef.current) {
console.log("set false");
firstRenderRef.current = false;
} else if (activeSlideRef.current) {
console.log("scroll");
activeSlideRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}
}, [activeSlide]);
const moveLeft = Math.max(0, activeSlide - 1);
const moveRight = Math.min(children.length - 1, activeSlide + 1);
return (
<React.Fragment>
<button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
<div id="test">
{children.map((child, i) => {
const active = i === activeSlide;
return (
<div className={`slide ${active ? "active" : ""}`} ref={active ? activeSlideRef : null} id={`slide-${i}`} key={`slide-${i}`}>
{child}
</div>
);
})}
</div>
<button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
</React.Fragment>
);
}
ReactDOM.render(
<Deck>
<div>slide 0 </div>
<div>slide 1 </div>
<div>slide 2 </div>
<div>slide 3 </div>
<div>slide 4 </div>
<div>slide 5 </div>
<div>slide 6 </div>
<div>slide 7 </div>
<div>slide 8 </div>
<div>slide 9 </div>
</Deck>,
document.getElementById("root")
);
Run Code Online (Sandbox Code Playgroud)
.slide {
height: 4em;
vertical-align: middle;
text-align: center;
}
#test {
overflow: scroll;
max-height: 10em;
}
.active {
font-weight: bold;
color: blue;
}
Run Code Online (Sandbox Code Playgroud)
gau*_*430 26
添加到已接受的答案并尝试回答问题的“应该”部分,使用 refs 进行 DOM 操作:
当然,使用查询选择器并不是不正确,如果您通常在反应世界中使用它,它不会破坏您的功能,但最好使用框架提供的东西,因为它在大多数情况下具有一些默认的好处。
归档时间: |
|
查看次数: |
55424 次 |
最近记录: |