[英]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.