【问题标题】:How do I avoid this stale closure?如何避免这种陈旧的关闭?
【发布时间】:2020-11-03 03:02:53
【问题描述】:

我正在构建一个简单的 React 滑块,它将通过 ref 向其父级公开内部方法,我在怀疑是陈旧的闭包时遇到了麻烦,但我无法完全理解实际发生的情况.希望有人能帮助我理解这里。

这是我想要工作的代码的简化版本:

const Slider = forwardRef((props, ref) => {
  const sliderRef = useRef();
  const [slides, dispatchSlides] = useReducer(reducer, []);

  sliderRef.current = {
    countSlides: () => {
      return slides.length
    },
  };

  useImperativeHandle(ref, () => sliderRef.current);

  return null;

安装此组件后,其子组件将使用 IntersectionObserver 渲染并填充 slides 减速器及其位置和可见性信息。这部分有效,因此为简单起见,我将其排除在此示例之外。为了我们的缘故,假设slides 在挂载后立即被对象填充,并且用户将在填充slides 之后很久之后从父组件手动调用countObjects

在父组件中,如果我从 ref 执行countSlides,我将始终看到slides.length === 0,无论实际存在多少张幻灯片。我认为这是因为原来的 countSlides 方法是一个陈旧的闭包。

现在,我不完全理解的是,如果我调整这条线:

  useImperativeHandle(ref, () => sliderRef.current);

到这里:

  useImperativeHandle(ref, () => () => {
    countSlides: () => sliderRef.current.countSlides()
  });

陈旧的关闭已修复,一切都按预期工作。但这是重复的代码,我只是不确定这两种情况有什么不同。我不想重复自己在 useImperativeHandle 钩子中重新定义许多方法,但更重要的是,我想了解上面两个示例之间的区别。

谢谢!

编辑添加完整示例:

https://codesandbox.io/s/ssr-slider-6ywf9

【问题讨论】:

  • 能否添加使用 Slider 组件的代码?我想看看它的用法
  • 如果可能的话,我希望看到您的组件的完整版本,因为我尝试使用您使用的两种方法,并且都对我有用。 codesandbox.io/s/use-imperative-handle-7ij5k?file=/src/App.tsx
  • 好的,所以我刚刚浏览了您的沙箱并弄清楚了为什么您的沙箱有效,而我的无效。您正在有效地创建与我所展示的工作相同的模式,但在不同的地方。这一行创建了一个闭包:onClick={() => {countRef1.current.increase();}} 解决了这个问题。但是.. 我的代码是这样做的:onClick={countRef1?.current?.increase}。另外,我用一个完整的例子更新了我的帖子来演示。所以,原来的问题依然存在,只是……移动了。

标签: javascript reactjs react-hooks


【解决方案1】:

正如您评论的那样,只有在写成onClick={ slider?.current?.prev }而不是onClick={() => { slider?.current?.prev() }}时才会出现问题

我已尝试使用我提供的沙盒,但遇到了同样的问题。

这里有几件事:

  1. useRef 本身不会触发重新渲染,这意味着即使更新了 ref,也不会发生重新渲染。
  2. 如果不重新渲染,绑定到 onClick 的内容将不会更新。

所以,如果我们写成onClick={slider?.current?.prev},会发生什么:

  1. ref 最初是未定义的,这意味着 onClick 也是未定义的
  2. 不会触发重新渲染,因此,即使 ref 更新为新值,onClick 仍保持未定义

但是,如果我们像 onClick={() => { slider?.current?.prev() }} 这样写,会发生什么:

  1. slider?.current?.prev 最初是未定义的
  2. onClick 绑定到该匿名函数
  3. slider?.current?.prev 已更新,我们有预期的功能
  4. 点击按钮时,调用函数,触发slider?.current?.prev的最新值

【讨论】:

    猜你喜欢
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    • 1970-01-01
    • 2023-03-29
    • 2013-04-20
    • 1970-01-01
    相关资源
    最近更新 更多