使用React组件中的旧上下文值的点击处理程序?

时间:2019-10-13 15:57:52

标签: reactjs react-hooks

我正在开发一个笔记应用程序。当前选择的笔记本是通过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
  }
}

有关更多信息,请参见此处的完整文件:

1 个答案:

答案 0 :(得分: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 now 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
}

如果您要删除整个应用程序中当前为“ “新”” 的注释,则可以创建一个DELETE_CURRENT_NOTEBOOK操作,该操作将删除当前设置为{{1 }}。