id
我无法回答是否为此使用 refs 的“应该”部分,除非您这样做,除非您将它们用于其他用途,否则您不需要这些值。
但你会这样做:
用于useRef(null)
创建参考。
const activeSlideRef = useRef(null);
把它放在Slide
当前活跃的
<Slide ref={i === activeSlide ? activeSlideRef : null} ...>
在您的useEffect
中,使用 ref 的current
属性
useEffect(() => {
if (activeSlideRef.current) {
activeSlideRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}
}, [activeSlide]);
(我认为activeSlide
这种效果是一个合理的依赖。你不能使用 ref,ref 本身不会改变......)
现场示例,为方便起见,我将您的一些组件转换为div
s :
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")
);
.slide {
height: 4em;
vertical-align: middle;
text-align: center;
}
#test {
overflow: scroll;
max-height: 20em;
}
.active {
font-weight: bold;
color: blue;
}
<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>
在您问过的评论中:
您知道是否可以在useEffect
此处禁用第一次渲染?
为了保留每个组件的非状态信息,有趣的是,您使用useRef
. 文档useRef
指出,它不仅适用于 DOM 元素引用,还适用于每个组件的非状态数据。所以你可以有
const firstRenderRef = useRef(true);
然后在你的useEffect
回调中,检查firstRenderRef.current
&mndash; 如果是true
,设置它false
,否则进行滚动:
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")
);
.slide {
height: 4em;
vertical-align: middle;
text-align: center;
}
#test {
overflow: scroll;
max-height: 10em;
}
.active {
font-weight: bold;
color: blue;
}
<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>
作为一个思想实验,我写了一个钩子来简化人体工程学:
function useInstance(instance = {}) {
// assertion: instance && typeof instance === "object"
const ref = useRef(instance);
return ref.current;
}
用法:
const inst = useInstance({first: true});
在useEffect
,如果inst.first
是真的,做inst.first = false;
;否则,进行滚动。
居住:
const {useEffect, useRef, useState} = React;
function useInstance(instance = {}) {
// assertion: instance && typeof instance === "object"
const ref = useRef(instance);
return ref.current;
}
function Deck({children}) {
const [activeSlide, setActiveSlide] = useState(0);
const activeSlideRef = useRef(null);
const inst = useInstance({first: true});
console.log("render");
useEffect(() => {
// *** After render, don't do anything, just remember we've seen the render
if (inst.first) {
console.log("set false");
inst.first = 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")
);
.slide {
height: 4em;
vertical-align: middle;
text-align: center;
}
#test {
overflow: scroll;
max-height: 10em;
}
.active {
font-weight: bold;
color: blue;
}
<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>