我有Scrollable组件,可能有可滚动的子代或大子代。我想限制两个组件中的onScroll
处理程序,但也要防止子级onScroll事件传播到父级。
我的尝试
import React from 'react;
import throttle from 'lodash.throttle'
/*
This component stops the scroll from propagting to the parent
But does not throttle, calls handleScroll way too many times
*/
const UnthrottledSubPage = () => {
const handleScroll = event => {
event.stopPropagation();
console.count('? SubPage: scrolled: ');
console.log(event);
};
return (<div onScroll={handleScroll}>Sub Page</div>);
};
/*
This component throttles the scroll alright...but I have no way
of stopping the scroll event from propagating to a scrollable parent
or grand parent.
*/
const ThrottledSubPageA = () => {
const handleScroll = event => {
event.stopPropagation(); // lineA: Called too late.
console.count('? SubPage: scrolled: ');
console.log(event);
};
return (<div onScroll={throttle(handleScroll, 1000)}>Sub Page</div>);
};
我“怀疑”,这种方法有时在ThrottledSubPageA
的油门正在等待时,没有调用handleScroll停止传播,而是调用了父级滚动,或者在已经合并时调用了父级滚动。导致此警告。
警告:出于性能原因,此合成事件被重用。如果 您看到的是,您正在访问方法
stopPropagation
已发布/无效的合成事件。这是一项无操作功能。如果你 必须保留原始的合成事件,使用event.persist()。 有关更多信息,请参见React event-pooling。
可运行的代码段,重现了这种情况。打开DevTools作为日志
// Scrollable Component, called within a Scrollable Grand Parent (App).
const SubPage = () => {
const handleScroll = event => {
event.stopPropagation();
console.count('? SubPage: scrolled: ');
console.log(event);
};
return (
<div
className="subpage"
style={{ height: '200px' }}
onScroll={throttle(handleScroll, 1000)}
>
<h1>Sub Page</h1>
{/* Lengthy Scrollble Content */}
<div className="content">Content</div>
</div>
);
};
const MainPage = props => {
const { height } = props;
return (
<div className="page" style={{ height }}>
<h1>Main Page</h1>
<SubPage />
</div>
);
};
// Scrollable Component, that has a Scrollable Grand Child.
const App = () => {
const handleScroll = event => {
console.count('? App: scrolled: ');
console.log(event);
};
return (
<section className="app" onScroll={throttle(handleScroll, 1000)}>
<MainPage height="200vh" />
<footer>Footer</footer>
</section>
);
};
ReactDOM.render(<App />, document.getElementById('container'));
/* Styles: Mostly Irrelevant to the question */
body {
padding: 0;
margin: 0;
overflow: hidden;
height: 100vh;
}
.app {
background: #ddd;
width: 100%;
height: 100vh;
overflow-y: scroll;
color: #2b2b2b;
padding: 1rem;
}
.page,
.subpage {
background: aquamarine;
border: solid 1px red;
padding: 2rem;
}
.subpage {
border: solid 1px black;
background: white;
overflow-y: scroll;
}
.content {
height: 200vh;
background: yellow;
}
footer {
background: black;
color: white;
display: block;
width: 100%;
padding: 1rem;
}
<div id="container"></div>
<script src="https://cdn.jsdelivr.net/npm/lodash.throttle@4.1.1/index.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
/*
This component doesn't throttle at all...and behaves like UnthrottledSubPage,
and whatever is within the throttle block is never called.
*/
const ThrottledSubPageB = () => {
const handleScroll = event => {
event.stopPropagation(); // lineA: Called alright.
throttle(()=> {
/* This block is never reached...*/
console.count('? SubPage: scrolled: ');
console.log(event);
}, 1000);
};
return (<div onScroll={handleScroll)}>Sub Page</div>);
};
如果我移动油门来完成昂贵的滚动任务,例如上面的ThrottledSubPageB
,则永远不会发生节流!再次,我“认为”(我可能是错误的),这可能是因为在每个滚动上创建了一个新函数,从而导致了新的油门,新的等待……这永远不会发生。我在日志中看不到它!
问题:,如何确保在滚动时调用油门,但仍然停止等待中的下一个滚动,使其传播到父级?