React Context API 不适用于单个应用程序对话框

React Context API doesn't work for single application dialog

我想创建一个对话框组件来渲染一次并在整个应用程序中使用它,而不是每次需要显示一个对话框时都创建一个对话框组件。

上下文 API 最初正确显示对话框。如果对话框只包含静态文本,那么它会正确显示。但是如果对话框包含任何动态元素,它不会对更改做出反应。

我使用自定义挂钩处理对话框。

这是一个带有静态文本的对话示例

export function useStaticTextDialog(callback) {
  const appContext = useContext(AppContext);

  const openDialog = () => appContext.openAppDialog(appDialogContext);
  const onDialogOk = () => {
    appContext.closeAppDialog(); // close unique app dialog first in case the callback uses it
    callback();
  };

  const appDialogContext = {
    title: 'Static text',
    content: 'This static text works.',
    onOk: onDialogOk,
    onClose: appContext.closeAppDialog,
  };

  return { open: openDialog };
}

它在某个地方被这样称呼:

 const staticTextDialog = useStaticTextDialog(callback);
 staticTextDialog.open();

单个组件在App组件中渲染一次

import AppDialog from 'components/appDialog';

function App() {
  return (
    <AppContextProvider>
      <Router />
      <AppDialog />
    </AppContextProvider>
  );
}

上下文很简单:

import React, { createContext, useState } from 'react';

const AppContext = createContext();

function AppContextProvider({ children }) {
  const [isAppDialogOpen, setIsAppDialogOpen] = useState(false);
  const [appDialogContext, setAppDialogContext] = useState({});

  const openAppDialog = appDialogContext => {
    setAppDialogContext(appDialogContext);
    setIsAppDialogOpen(true);
  };
  const closeAppDialog = () => setIsAppDialogOpen(false);

  const appContextValue = {
    isAppDialogOpen,
    appDialogContext,
    openAppDialog,
    closeAppDialog,
  };

  return <AppContext.Provider value={appContextValue}>{children}</AppContext.Provider>;
}

export { AppContext, AppContextProvider };

好吧,到目前为止一切正常。但是 如果将动态元素添加到对话框中,它不会对更改做出反应 并显示陈旧的值:

export function useDynamicTextDialog(callback) {
  const appContext = useContext(AppContext);
  const [name, setName] = useState();

  const openDialog = newName => {
    setName(newName);
    appContext.openAppDialog(appDialogContext);
  };
  const onDialogOk = () => {
    appContext.closeAppDialog(); // close unique app dialog first in case the callback uses it
    callback(name);
  };

  const appDialogContext = {
    title: 'Overwrite warning',
    content: `The name "${name}" already exists. Are you sure you want to overwrite it?`,
    onOk: onDialogOk,
    onClose: appContext.closeAppDialog,
  };

  return { open: openDialog };
}

如果对话框包含表单元素(如文本框或下拉列表),该对话框也不起作用。它不会对用户的新值做出反应。

我发现这种行为出乎意料,我认为它应该对更改做出反应并正常工作。 当呈现多个对话框组件时,同样的方法实际上有效,每次需要一个对话框时。在这种情况下不使用上下文 API。示例:

import DialogTemplate from 'components/dialogTemplate';

export function useDynamicTextDialog(callback) {
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const [name, setName] = useState();

  const openDialog = newName => {
    setName(newName);
    setIsDialogOpen(true);
  };
  const closeDialog = () => setIsDialogOpen(false);
  const onDialogOk = () => {
    closeDialog ();
    callback(name);
  };

  const dialogContext = {
    title: 'Overwrite warning',
    content: `The name "${name}" already exists. Are you sure you want to overwrite it?`,
    onOk: onDialogOk,
    onClose: closeDialog,
  };

  const Dialog = <DialogTemplate dialogContext={dialogContext} />;

  return { Dialog, open: openDialog };
}

然后它在某处打开,就像单个应用程序对话框一样:

 const dynamicTextDialog = useDynamicTextDialog(callback);
 dynamicTextDialog.open();

但在这种情况下必须呈现每个对话框组件:

function Component() {
  const { dynamicTextDialog } = useDynamicTextDialog();

  return (
    <div>
      {...more elements}
      {applicationClosingWarningDialog.Dialog}
    </div>
  );
}

因此,如果使用 20 个对话框,则必须渲染 20 个对话框组件,尽管它们除了内容外几乎完全相同。

这就是为什么我想为整个应用程序只使用一个对话框组件,每次都重复使用它并且只更改内容。但是上下文 API 不起作用。

为什么它适用于对话框模板的示例,但不适用于带上下文的应用程序对话框的示例,即使数据的处理方式相同?

如何让它与 Context API 一起工作?

上下文 API 不会对对话框的更改做出反应,即使它的使用方式与工作的模板组件相同。

因此,必须在对话框更改时专门触发渲染:

  useEffect(() => {
    appContext.setAppDialogContext(dialogContext);
  }, [name]);

完整的钩子:

export function useDynamicTextDialog(callback) {
  const appContext = useContext(AppContext);
  const [name, setName] = useState();
  
  useEffect(() => {
    appContext.setAppDialogContext(dialogContext);
  }, [name]);

  const openDialog = newName => {
    setName(newName);
    appContext.openAppDialog(appDialogContext);
  };
  const onDialogOk = () => {
    appContext.closeAppDialog(); // close unique app dialog first in case the callback uses it
    callback(name);
  };

  const appDialogContext = {
    title: 'Overwrite warning',
    content: `The name "${name}" already exists. Are you sure you want to overwrite it?`,
    onOk: onDialogOk,
    onClose: appContext.closeAppDialog,
  };

  return { open: openDialog };
}

带模板的对话框演示和单对话框应用程序:

https://ams777.github.io/react-single-app-dialog

演示代码:

https://github.com/AMS777/react-single-app-dialog