React Context API 不适用于单个应用程序对话框
Posted
技术标签:
【中文标题】React Context API 不适用于单个应用程序对话框【英文标题】:React Context API doesn't work for single application dialog 【发布时间】:2021-05-05 00:49:30 【问题描述】:我想创建一个对话框组件来渲染一次并在整个应用程序中使用它,而不是每次需要显示对话框时都创建一个对话框组件。
Context 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 个对话框组件,尽管它们除了内容几乎相同。
这就是为什么我想在整个应用程序中只使用一个对话框组件,每次都重用它并且只更改内容。但是 Context API 不起作用。
为什么它适用于带有对话框模板的示例,但不适用于带有上下文的应用程序对话框的示例,即使数据处理方式相同?
如何让它与 Context API 一起工作?
【问题讨论】:
【参考方案1】:上下文 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
【讨论】:
以上是关于React Context API 不适用于单个应用程序对话框的主要内容,如果未能解决你的问题,请参考以下文章
getCurrentPosition 中的 React-native-maps setState 不适用于我的 url API
对外部 API 的 HTTP 请求适用于 .NET,但不适用于 React(CORS 问题)