[英]React Hooks - set last value?
我正在尝试编写基本的钩子来在滚动时获取 currentScroll、lastScroll、scrollSpeed。
function useDocScroll() {
const isClient = typeof window === "object"
function getScroll() {
return isClient
? window.pageYOffset || document.documentElement.scrollTop
: undefined
}
const [docScroll, setDocScroll] = useState(getScroll)
const [lastScroll, setLastScroll] = useState(null)
const [scrollSpeed, setScrollSpeed] = useState(Math.abs(docScroll - lastScroll))
useEffect(() => {
if (!isClient) {
return false
}
function handleScroll() {
setDocScroll(getScroll())
setLastScroll(getScroll()) // <-- why is this working?
// setLastScroll(docScroll) // <-- why is this not working?
setScrollSpeed(Math.abs(docScroll - lastScroll)) // <-- why is this not working?
}
window.addEventListener("scroll", handleScroll)
}, [])
return [docScroll, lastScroll, scrollSpeed]
}
似乎当我执行setLastScroll(getScroll())
,它可以很好地保存最后一个滚动值。
但我不明白,因为当handleScroll()
触发时, getScroll()
值不应该保持不变吗? 我不明白为什么setDocScroll(getScroll())
和setLastScroll(getScroll())
有不同的值。
另外,我想我可以做setLastScroll(docScroll)
,意思是“用当前的 docScroll 值设置 lastScroll 值”,但它只是在 docScroll 值更改时打印“0”。
为什么是这样? 我想更好地理解。
+) 而且我无法获得由docScroll
和lastScroll
计算的scrollSpeed
,但我不知道如何获得这些值。
我认为为什么您的代码不起作用是因为以下两个原因:
setDocScroll
之后直接使用docScroll
将不起作用,因为 setState 是异步任务。 不能保证在执行下docScroll
语句之前更新docScroll
0
。 由于document.documentElement
指向html
元素并且里面没有滚动。 所以你收到0
解决方案:
您不需要多个useState
。 由于滚动事件发射太频繁,我认为使用useReducer
来减少渲染次数是个好主意。 无论是在根级别还是在某个元素内部,了解滚动发生的位置很重要。
对于以下解决方案,我提出了:
如果滚动发生在根级别(html 元素),则无需将元素传递给useDocScroll
。 如果滚动发生在特定元素内,则需要传递元素引用。
const initState = {
current: 0,
last: 0,
speed: 0,
};
function reducer(state, action) {
switch (action.type) {
case "update":
return {
last: state.current,
current: action.payload,
speed: Math.abs(action.payload - state.current) || 0,
};
default:
return state;
}
}
const isClient = () => typeof window === "object";
function useDocScroll(element = document.documentElement) {
const [{ current, last, speed }, dispatch] = useReducer(reducer, initState);
function getScroll() {
return isClient() ? element.scrollTop : 0;
}
function handleScroll() {
dispatch({ type: "update", payload: getScroll() });
}
useEffect(() => {
if (!isClient()) {
return false;
}
element.addEventListener("scroll", handleScroll);
return () => element.removeEventListener("scroll", handleScroll);
}, []);
return [current, last, speed];
}
例子:
如果滚动发生在窗口内
const {current, last, speed} = useDocScroll()
如果滚动发生在特定元素
const {current, last, speed} = useDocScroll(document.getElementById("main"))
由于关闭,它不起作用:
useEffect(() => {
if (!isClient) {
return false;
}
function handleScroll() {
setDocScroll(getScroll());
// On calling handleScroll the values docScroll & lastScroll
// are always will be of the first mount,
// the listener "remembers" (closures) such values and never gets updated
setLastScroll(docScroll);
setScrollSpeed(Math.abs(docScroll - lastScroll));
// v Gets updated on every call
setLastScroll(getScroll());
}
// v Here you assigning a callback which closes upon the lexical scope of
// docScroll and lastScroll
window.addEventListener('scroll', handleScroll);
}, []);
要修复它,一个可能的解决方案可以是参考( useRef
)和功能setState
。
例如:
setScrollSpeed(lastScroll => Math.abs(getScroll() - lastScroll))
不知道为什么lastScrolled
需要:
function useDocScroll() {
const [docScroll, setDocScroll] = useState(getScroll());
const lastScrollRef = useRef(null);
const [scrollSpeed, setScrollSpeed] = useState();
useEffect(() => {
if (!isClient) {
return false;
}
function handleScroll() {
const lastScroll = lastScrollRef.current;
const curr = getScroll();
setScrollSpeed(Math.abs(getScroll() - (lastScroll || 0)));
setDocScroll(curr);
// Update last
lastScrollRef.current = curr;
}
window.addEventListener('scroll', handleScroll);
}, []);
return [docScroll, lastScrollRef.current, scrollSpeed];
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.