首页 > 解决方案 > 如何阻止 Throttled Scroll 从传播到父级?

问题描述

我有 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>);
};

我“怀疑”,这种方法有时在ThrottledSubPageAs 油门等待时,不会调用 handleScroll 来停止传播,而是调用父滚动,或者在它已经被池化时调用。导致此警告。

警告:出于性能原因,此合成事件被重用。如果您看到此内容,则表示您正在访问已stopPropagation发布/无效合成事件的方法。这是一个无操作功能。如果您必须保留原始合成事件,请使用 event.persist()。有关更多信息,请参阅React 事件池

重现此案例的可运行片段。为日志打开 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永远不会发生!我再次“认为”(我可能错了)这可能是因为每次滚动都会创建一个新功能,从而导致新的油门,新的等待......永远不会出现。我在日志中看不到它!

问题:,如何确保在滚动时调用节流阀,但仍会在等待中停止下一个滚动,从传播到父级?

标签: reactjsevent-handlingthrottling

解决方案


推荐阅读