[英]React hook not updated in function listened on scroll event
我有一個在scroll事件上監聽的函數handleScroll
。 Thsi函數必須更新isFetching
(它開始為false並且必須更改布爾值)。
正如console.log
所示,正確監聽函數handleScroll
。 但是, isFetching
總是錯誤的。 似乎從未讀過setIsFetching
。 我認為另一種選擇就像eventListener凍結了handleScroll函數的第一個版本。
我該怎么做才能更新該函數中的鈎子? 這是代碼和代碼框的簡化版本:
/* <div id='root'></div> */
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const debounce = (func, wait, immediate) => {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};
const App = () => {
const [isFetching, setIsFetching] = useState(false);
const handleScroll = debounce(() => {
setIsFetching(!isFetching);
console.log({ isFetching });
}, 300);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return <div style={{ height: "1280px" }}>Hello world</div>;
};
const root = document.getElementById("root");
if (root) ReactDOM.render(<App />, root);
UPDATE
我把一個空數組作為useEffect
的第二個參數,因為我希望第一個param函數只在componentDidMount()上觸發一次
為了從useEffect
回調內部監聽狀態的變化(當你沒有跟蹤任何道具更新時),你可以將狀態保存在組件范圍之外的變量中,並直接使用它而不是狀態。
在這里你有代碼:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const debounce = (func, wait, immediate) => {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
};
let isFetchingState;
const App = () => {
const [isFetching, setIsFetching] = useState(false);
isFetchingState = isFetching;
const handleScroll = debounce(() => {
setIsFetching(!isFetchingState);
console.log({ isFetchingState });
}, 300);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return <div style={{ height: "1280px" }}>Hello world</div>;
};
const root = document.getElementById("root");
if (root) ReactDOM.render(<App />, root);
添加isFetching
作為isFetching
的依賴useEffect
雖然我無法提供深入的解釋,但我可以說當你說效果不依賴於任何東西時,你基本上在React中useEffect
了React,通過提供一個空的依賴數組,總是很好地傳遞包含在中的所有變量你的影響。
此外,每次re-render
組件時都會創建一個新函數,以避免在useEffect
移動函數或將其包裝在useCallback
,而不會創建重新創建函數,除非依賴項數組中的某些內容發生更改
useEffect(
() => {
const handleScroll = debounce(() => {
setIsFetching(prevState => !prevState);
console.log({ isFetching });
}, 300);
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
},
[isFetching]
);
或者使用useCallback
const handleScroll = useCallback(
debounce(() => {
setIsFetching(prevState => !prevState);
console.log({ isFetching });
}, 300),
[isFetching]
);
useEffect(
() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
},
[isFetching]
);
您已將空數組作為useEffect的第二個參數傳遞。 這就是你在運行后不再被稱為useEffect的關鍵問題。
要解決這個問題,請不要傳遞第二個參數。
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
});
或者,每當要更改的屬性時調用useEffect:
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [isFetching]); // re-run useEffect on isFetching changed
這與我們在componentDidUpdate中的操作類似:
if (prevState.count !== this.state.count) {
// do the stuff
有關更多詳細信息,請參閱文檔本身。
來自文檔的說明:
如果要運行效果並僅將其清理一次(在裝載和卸載時),則可以將空數組([])作為第二個參數傳遞。 這告訴React你的效果不依賴於來自props或state的任何值,所以它永遠不需要重新運行。 這不作為特殊情況處理 - 它直接遵循依賴項數組的工作方式。
如果傳遞一個空數組([]),效果中的props和state將始終具有其初始值。 雖然傳遞[]作為第二個參數更接近熟悉的componentDidMount和componentWillUnmount心理模型,但通常有更好的解決方案來避免經常重新運行效果。 此外,不要忘記React推遲運行useEffect直到瀏覽器繪制完成后,所以做額外的工作不是問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.