我试图在react中使用交集观测器来实现无限滚动体,但我面临的问题是,在交集观测器的回调中,我无法读取当前“页面”和“列表”的最新值,以便为下一页获取数据。
import ReactDOM from "react-dom";
import "./styles.css";
require("intersection-observer");
const pageSize = 30;
const threshold = 5;
const generateList = (page, size) => {
let arr = [];
for (let i = 1; i <= size; i++) {
arr.push(`${(page - 1) * size + i}`);
}
return arr;
};
const fetchList = page => {
return new Promise(resolve => {
setTimeout(() => {
return resolve(generateList(page, pageSize));
}, 1000);
});
};
let options = {
root: null,
threshold: 0
};
function App() {
const [page, setPage] = useState(1);
const [fetching, setFetching] = useState(false);
const [list, setlist] = useState(generateList(page, pageSize));
const callback = entries => {
if (entries[0].isIntersecting) {
observerRef.current.unobserve(
document.getElementById(`item_${list.length - threshold}`)
);
setFetching(true);
/* at this point neither the 'page' is latest nor the 'list'
*they both have the initial states.
*/
fetchList(page + 1).then(res => {
setFetching(false);
setPage(page + 1);
setlist([...list, ...res]);
});
}
};
const observerRef = useRef(new IntersectionObserver(callback, options));
useEffect(() => {
if (observerRef.current) {
observerRef.current.observe(
document.getElementById(`item_${list.length - threshold}`)
);
}
}, [list]);
return (
<div className="App">
{list.map(l => (
<p key={l} id={`item_${l}`}>
{l}
</p>
))}
{fetching && <p>loading...</p>}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);当前行为:‘页面’和'list‘的值总是等于初始状态,而不是最新值。无限卷轴在第2页后不起作用。
预期行为:回调函数中的应该读取状态'page‘和'list’的更新值。
下面是这个演示 https://codesandbox.io/s/sweet-sun-rbcml?fontsize=14&hidenavigation=1&theme=dark的工作沙箱
发布于 2020-01-04 16:23:44
这里主要有两个问题:闭包和直接查询DOM。
要解决闭包问题,请使用函数式useState和引用:
const listLengthRef = useRef(list.length);
const pageRef = useRef(page);
const callback = useCallback(entries => {
if (entries[0].isIntersecting) {
observerRef.current.unobserve(
document.getElementById(`item_${listLengthRef.current - threshold}`)
);
setFetching(true);
fetchList(pageRef.current + 1).then(res => {
setFetching(false);
setPage(page => page + 1);
setlist(list => [...list, ...res]);
});
}
}, []);
const observerRef = useRef(new IntersectionObserver(callback, options));
useEffect(() => {
listLengthRef.current = list.length;
}, [list]);
useEffect(() => {
pageRef.current = page;
}, [page]);尽管这段代码可以工作,但是您应该用reference替换document.getElementById,在本例中,它将是对页面最后一个元素的引用。
发布于 2020-01-04 16:11:59
您可以使用React setState 回调法来保证您将收到前面的值。
按以下方式更新您的callback函数,它应该可以工作。
const callback = entries => {
if (entries[0].isIntersecting) {
setFetching(true);
setPage(prevPage => {
fetchList(prevPage + 1).then(res => {
setFetching(false);
setlist(prevList => {
observerRef.current.unobserve(document.getElementById(`item_${prevList.length - threshold}`));
return ([...prevList, ...res]);
});
})
return prevPage + 1;
})
}
};发布于 2020-01-04 16:21:57
我认为问题是由于裁判一直引用老观察者。每次更新依赖项时,都需要刷新观察者。它与js中的闭包有关。我会更新你的应用程序,将回调移动到useEffect中
function App() {
const [page, setPage] = useState(1);
const [fetching, setFetching] = useState(false);
const [list, setlist] = useState(generateList(page, pageSize));
const observerRef = useRef(null);
useEffect(() => {
const callback = entries => {
if (entries[0].isIntersecting) {
observerRef.current.unobserve(
document.getElementById(`item_${list.length - threshold}`)
);
setFetching(true);
/* at this point neither the 'page' is latest nor the 'list'
*they both have the initial states.
*/
console.log(page, list);
fetchList(page + 1).then(res => {
setFetching(false);
setPage(page + 1);
setlist([...list, ...res]);
});
}
};
observerRef.current = new IntersectionObserver(callback, options);
if (observerRef.current) {
observerRef.current.observe(
document.getElementById(`item_${list.length - threshold}`)
);
}
}, [list]);
return (
<div className="App">
{list.map(l => (
<p key={l} id={`item_${l}`}>
{l}
</p>
))}
{fetching && <p>loading...</p>}
</div>
);
}https://stackoverflow.com/questions/59589835
复制相似问题