单击处理程序是否在React组件中使用旧的上下文值?

问题描述 投票:0回答:1

我正在做笔记应用程序。当前选择的笔记本是通过reducer设置的,并且该值通过上下文API传递到应用中的各个组件。

虽然我看到一些奇怪的行为,但不确定发生了什么。

我有一个带有删除选项的右键单击上下文菜单。在笔记本上单击鼠标右键时,它将通过调用dispatch并执行操作来更新当前笔记本,从而选择该笔记本。同时,通过上下文API检索当前笔记本。

当我右键单击笔记本时,将分派SET_CURRENT_NOTEBOOK操作。新选择的笔记本反映在用户界面中。在删除上下文菜单项的单击处理程序中,它调用一个函数来开始删除操作。

但是,当我单击删除菜单项时,它仍然具有先前选择的笔记本作为当前笔记本。这对我来说没有任何意义,因为UI中的其他组件已重新渲染以显示当前选择的新笔记本。

这里是Sidebar组件,其中包含一组Notebook组件。

function Sidebar() {
  const { notebooks, currentNotebook } = useContext(Notes.State);
  const notesDispatch = useContext(Notes.Dispatch);

  function onClickNotebook(notebook) {
    notesDispatch({ type: SET_CURRENT_NOTEBOOK, payload: notebook });
  }

  function onClickDelete() {
    console.log(currentNotebook); // logs previously selected notebook instead of the current one
  }

  return (
    {notebooks.map(notebook => (
      <Notebook onClick={onClickNotebook} onDelete={onClickDelete} />
    )}
  );
}

这是Notebook组件的摘录:

function Notebook({ onClick, onClickDelete }) {
  const contextMenu = remote.Menu.buildFromTemplate([
    {
      label: 'Delete',
      click: onDelete
    },
    {
      label: 'Rename',
      click: onRename
    }
  ]);

  function showPopupMenu() {
    onClick(notebook); // this will cause the SET_CURRENT_NOTEBOOK action to be dispatched
    contextMenu.popup(); // this will show an Electron context menu. When the delete button is clicked, the current notebook hasn't been updated even though other components in the UI are showing the new current notebook
  }
}

有关更多上下文,可以在这里找到完整的文件:

reactjs react-hooks
1个回答
0
投票

逐步执行代码:

function Sidebar() {
  // 1) You get the value for currentNotebook for the first time. Let's call it current01
  const { notebooks, currentNotebook } = useContext(Notes.State);
  const notesDispatch = useContext(Notes.Dispatch);

  function onClickNotebook(notebook) {
    notesDispatch({ type: SET_CURRENT_NOTEBOOK, payload: notebook });
  }

  // 2) this guy holds a reference to current01, Let's call it delete01
  function onClickDelete() {
    console.log(currentNotebook);
  }

  return (
    {notebooks.map(notebook => (
      // 3) every <Notebook /> instance now has a reference to delete01, which has a reference to current01
      // They also get a reference for a different notebook each
      <Notebook notebook={notebook} onClick={onClickNotebook} onDelete={onClickDelete} />
    )}
  );
}

function Notebook({ onClick, onClickDelete, notebook }) {
  const contextMenu = remote.Menu.buildFromTemplate([
    {
      label: 'Delete',
      // 4) now the contextMenu has a reference to delete01, which will delete current01
      click: onDelete
    },
    {
      label: 'Rename',
      click: onRename
    }
  ]);

  function showPopupMenu() {
    // 5) updates the context for Notes.State, which changes currentNotebook and causes everything in the UI to be updated. So they all have current02, delete02, etc.
    // BUT the menu is still holding a reference to delete01, which points to current01
    onClick(notebook);
    contextMenu.popup();
  }
}

这似乎是预期的行为。假设您打开了contextMenu,但应用程序中的其他地方发生了一些更新currentNotebook(例如,它是一个多用户应用,其他人做了一些更改currentNotebook的操作),应该怎么办?您是否打算删除哪个 currentNotebook?应用程序的currentNotebook版本还是您手头的版本?我猜你是说后者...

[您陷入了其他用户弄乱“当前”笔记本的状态,而同时又试图删除“旧”当前]的情况。

因此,如果要删除手头的便笺,应告知删除功能它是哪一个便笺:

{
  label: 'Delete',
  click: () => onDelete(notebook) // of course you're also gonna have to change the implementation of onClickDelete and the related reducer
}

如果要删除整个应用程序中当前为“ new”当前的音符

,则可以创建DELETE_CURRENT_NOTEBOOK操作,该操作将删除当前设置为currentNotebook的所有内容。
© www.soinside.com 2019 - 2024. All rights reserved.