使用自定义 Hook 事件处理反应陈旧状态

Posted

技术标签:

【中文标题】使用自定义 Hook 事件处理反应陈旧状态【英文标题】:React Stale State with Custom Hook Event Handling 【发布时间】:2021-06-24 17:55:07 【问题描述】:

我似乎无法理解 React 中的陈旧状态问题,因为它与事件处理程序和钩子有关。我从概念上理解正在发生的事情 - 有一个闭包正在捕获状态值的起始值,并且没有在我期望的时候更新。

我正在创建一个 NavBar 组件,我想在该组件上添加键盘控件以允许访问子菜单等。我将展示代码,然后描述正在发生/未发生的事情。我还将链接到一个代码框,以便于分叉和调试。

CodeSandbox

导航栏

const NavBar: React.FC<Props> = ( children, label ) => 
  const 
    actions:  createNavItemRef ,
    state:  activeSubMenuIndex, navBarRef ,
   = useNav();

  console.log('NAV.BAR',  activeSubMenuIndex );

  return (
    <nav aria-label=label ref=navBarRef>
      <NavList aria-label=label role="menubar">
        children // NavItems
      </NavList>
    </nav>
  );
;

导航项

const NavItem: React.FC<Props> = ( children, hasSubMenu, to ) => 
  const ChildrenArray = React.Children.toArray(children);
  const 
    actions:  handleSelectSubMenu ,
    state:  activeSubMenuIndex ,
   = useNav();

  const handleSubMenuToggle = () => 
    handleSelectSubMenu(index);
  ;

  return (
    <li ref=ref role="none">
      <>
        <ParentButton
          aria-expanded=activeSubMenuIndex === index
          aria-haspopup="true"
          onClick=handleSubMenuToggle
          role="menuitem"
          tabIndex=index === 0 ? 0 : -1
        >
         ChildrenArray.shift()
        </ParentButton>
        ChildrenArray.pop()
      </>
    </li>
  );
;

使用导航

function useNav() 
  const navBarRef = useRef<htmlUListElement>(null);    
  const [activeSubMenuIndex, setActiveSubMenuIndex] = useState<number | undefined>();

  const handleSelectSubMenu = (index?: number) => 
    if (!index || activeSubMenuIndex === index) 
      setActiveSubMenuIndex(undefined);
     else 
      setActiveSubMenuIndex(index);
    
  ;

  useEffect(() => 
    const navbar = navBarRef?.current;

    navbar?.addEventListener('keydown', () => 
      console.log("UseNav",  activeSubMenuIndex );
    );

    // return () => remove event listener
  , [navBarRef, activeSubMenuIndex]);

  return 
    actions: 
      createNavItemRef,
      handleSelectSubMenu,
    ,
    state: 
      activeSubMenuIndex,
      navBarRef,
    ,
  ;

这是我设置的精简版。最终,这就是正在发生的事情。

期待 我点击第一个NavItem,它变得专注。我按下箭头键(例如),日志UseNav activeSubMenuIndex ) 正确注销为undefined

然后我单击包含子菜单的 NavItem。 activeSubMenuIndex 更新并在 NavItem 中显示正确的子菜单(基于 activeSubMenuIndex === index 条件)。

但是,我希望NavBar activeSubMenuIndex ) 在单击此 NavItem 时也会注销。但事实并非如此。

在子菜单可见的情况下,我按了另一个箭头键,当显示UseNav 日志时,我希望它包含正确的activeSubMenuIndex 值,但它仍然是undefined

最终,我需要在 NavBar 上为 keyPress 添加EventListeners,以便在整个过程中分配键盘导航。但是,如果我什至无法在这个 MVP 级别正确更新状态值,那么如果不让以后的工作和调试变得更加麻烦,我就无法真正前进。

我知道这是一个过时状态的问题,但我找不到任何关于这个主题的好文章,而不仅仅是在同一个文件中增加一个数字。因此,任何有助于最终突破这堵墙的帮助都将是惊人的。

谢谢!

【问题讨论】:

【参考方案1】:

看起来这个问题源于使用了useNav() 钩子。当我在NavBar 内部调用这个钩子时,引用和值被实例化一次。当我在NavItem 中再次调用钩子时,这些相同的引用和值将再次实例化。

在这种情况下,与其使用钩子,不如将其包装在上下文中更有意义,以便将逻辑与 UI 隔离开来,但保持组件在其数据源中的一致性。

【讨论】:

以上是关于使用自定义 Hook 事件处理反应陈旧状态的主要内容,如果未能解决你的问题,请参考以下文章

Java事件处理机制(自定义事件)

Java事件处理机制(自定义事件)

java自定义事件,线程a如何每一秒钟触发一个事件,然后另一个线程b监听之,并作出反应?

使用React useContext hook进行分派调用后,反应状态不会更新

Android Hook技术

自定义按钮事件处理类