【问题标题】:Disabling button through props cancels its onClick通过道具禁用按钮会取消其onClick
【发布时间】:2021-06-30 05:33:43
【问题描述】:

TL;DR:转到react code sandbox 并尝试单击任一按钮几次。 Buggy 没有到达幻灯片的结尾。


我正在制作一个水平滚动的幻灯片。

它由一个带有overflow: auto容器 组成,其中包含一个水平溢出它的track。每个 slide 都是 track 的子级,并且与视口一样宽。 容器有一个索引状态,表示哪个幻灯片在视图中:

const slides = [/* some array of json data */]
const [index, setIndex] = useState(0)
const track = useRef(null)
const container = useRef(null)

return (
  <div ref={container} onScroll={onScroll}>
    <div ref={track}>
      {slides.map(() => (
        <div></div>
      ))}
    </div>
  </div>
)

要跟踪查看的幻灯片,有一个onScroll 函数:

const onScroll = () => {
  const newIndex = // math to get index from DOM
  if (index !== newIndex)
    setIndex(newIndex)
}

还有一个onClick 功能允许用户自动滚动到下一张幻灯片

const onClick = () => {
  track.current.children[index + 1].scrollIntoView({
    behavior: "smooth"
  })
}

这个onClick 被传递给一个按钮组件:

const BuggyButton = ({ disabled, onClick }) => (
  <button type="button" disabled={disabled} onClick={onClick}>
    click me
  </button>
)

当用户到达最后一张幻灯片时,按钮被禁用:

<BuggyButton onClick={onClick} disabled={index === slides.length - 1} />

问题:一旦&lt;BuggyButton/&gt; 被禁用,scrollIntoView 就会停止(过早地)。 为什么会这样?

这是在代码沙箱中完全重现和简化的问题:https://codesandbox.io/s/disable-button-cancels-scroll-270hz?file=/src/App.js

这里尽可能接近在 vanilla JS 中重现该问题:https://codesandbox.io/s/disable-button-cancels-scroll-vanilla-018mb


如果您只是在寻找解决方案(而不是想了解正在发生的事情,代码沙箱中有一个 &lt;ProperButton/&gt; 组件可以正常工作。带着这个问题,我试图了解 什么反应在幕后会导致浏览器中断滚动并保持其位置而不是将其重置为零。

【问题讨论】:

  • 只需将 BuggyButton 放在 App 内
  • @ludwiguer 我希望了解为什么这是一个问题,而不是如何解决它。此外,在真实的代码库中,我将拥有一个更复杂的按钮组件,我不能只在另一个组件的 render 中声明它。
  • @ludwiguer 然而,有趣的是,它在 App 内部而不是外部声明时有效。知道为什么吗?

标签: javascript reactjs


【解决方案1】:

总之,滚动停止是因为状态改变和组件重新渲染

if (index !== newIndex) {
  setIndex(newIndex);
}

这与 React 如何在组件位于闭包内和不在闭包内时处理协调有关,这就是为什么当您将按钮放在应用程序函数内部和外部时它的行为不同的原因。

您可以在此处阅读更多相关信息

https://reactjs.org/docs/reconciliation.html

https://overreacted.io/how-are-function-components-different-from-classes/

https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often

【讨论】:

  • 但是滚动是按钮的兄弟,而不是按钮本身......按钮重新渲染如何对其兄弟产生副作用?
  • 如果我理解正确的话,当状态改变时,反应重新渲染,然后对状态和/或道具改变的所有组件进行协调阶段。如果在这个阶段结束时,虚拟 dom 没有改变 (ProperButton),它就会停在那里。另一方面,如果预期的 DOM 输出不同(BuggyButton),则 react 删除适当的 DOM 节点并用新节点替换它们。这是据我了解,然后我不得不跳到“这就是滚动停止的原因”。
  • 我不明白为什么它会停止滚动,因为唯一需要替换的 DOM 节点(我理解的方式)是可滚动元素之外的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2011-03-12
  • 2016-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多