自动完成中的 React Material UI 打开模式失去焦点
Posted
技术标签:
【中文标题】自动完成中的 React Material UI 打开模式失去焦点【英文标题】:React Material UI open modal from within autocomplete lose focus 【发布时间】:2020-07-19 09:51:18 【问题描述】:我正在使用 material-ui 库,我需要一个自动完成功能,其中自动完成功能中的每个项目都是可点击的并打开一个模式。
一般的结构是:
const ModalBtn = () =>
...
return (
<>
<button ... (on click set modal to open)
<Modal ...
</>
);
const AutoCompleteWithBtns = () =>
return (
<Autocomplete
renderTags=(value, getTagProps) =>
value.map((option, index) => <ModalBtn />)
...
/>
);
注意ModalBtn是一个组件,不能分为Button和Modal两个组件。
问题是,当您单击模式内的按钮时 - 焦点保持在自动完成内,并且模式永远不会获得焦点(如果我在模式内有输入 - 我不能在里面写任何东西)。
尝试了所有标准的自动完成/模态焦点相关的道具(disableEnforceFocus
、disableEnforceFocus
等...),但没有任何效果。
这是working codesandbox example。如您所见 - 如果您单击不在自动完成组件内的按钮 - 一切正常(您可以在输入字段中添加文本)。如果您单击自动完成内的按钮 - 模式内的输入字段不可编辑(您失去焦点)。
这是一个问题示例:
【问题讨论】:
你能检查一下沙箱吗,它不工作 @Sabbin 对此感到抱歉,现在应该可以了。 是的,它有效,只是有一个问题,你需要两个具有相同功能的独立模态吗?问题是您会同时打开多个吗? 应该支持多个模式,但重要的是我需要从自动完成中打开模式(我假设一旦解决了这个问题,嵌套模式也应该解决) . @RyanCogswell 确实如此 :) 谢谢!我通常会等到最后一天才能获得赏金奖励,以便更多地了解答案。 【参考方案1】:您的代码中的问题是,Modal 是从 AutoComplete
组件的标记内呈现的,由于组件的可见性,这不是正常的,组件的层次结构是问题。
修复方法是将Modal
移动到FixedTags
组件中,并将打开的处理程序传递给renderTags
属性中的ModalBtn
;
我已使用工作变体 HERE 更新了您的沙盒
变化如下
const ModalBtn = ( handleOpen ) => (
<button type="button" onClick=handleOpen>
Open Modal (not working)
</button>
);
const FixedTags = function()
const classes = useStyles();
const [modalStyle] = React.useState(getModalStyle);
const [open, setOpen] = React.useState(false);
const handleOpen = () =>
setOpen(true);
;
const handleClose = () =>
setOpen(false);
;
return (
<>
<Autocomplete
multiple
options=autoCompleteItems
getOptionLabel=option => option.title
defaultValue=[autoCompleteItems[1], autoCompleteItems[2]]
renderTags=(value, getTagProps) =>
value.map((option, index) => <ModalBtn handleOpen=handleOpen />)
style= width: 500
renderInput=params => (
<TextField
...params
label="Fixed tag"
variant="outlined"
placeholder="items..."
/>
)
/>
<Modal open=open onClose=handleClose>
<div style=modalStyle className=classes.paper>
<h2 style= color: "red" >This one doesn't work</h2>
<p>Text field is not available</p>
<TextField label="Filled" variant="filled" /> <br />
<br />
<br />
<FixedTags label="Standard" />
</div>
</Modal>
</>
);
;
【讨论】:
将模式本身移到自动完成标签之外确实是一个有效的解决方案,但不是一个好的解决方案。ModalTbn
是一个完整的(可以说是一个封闭的组件),它呈现一个按钮并在单击时打开一个模式。这两个是耦合的,我真的不想把它们分开。
实际上您的代码存在内存泄漏问题,因为假设您在一个自动完成功能中有 1000 个标签,那么您在该自动完成功能中有 1000 个模式。当您一次只能访问一个组件时,为什么要渲染 1000 个组件?假设它嵌套了 6 个级别,每个级别 1000。您将有 6000 个模态组件和 6 个渲染?您无法在任何时候从同一级别打开 2 个模态,为什么要打开它们?这就是 react 的美妙之处,每个元素都可以是一个组件本身,将按钮保留在一个组件中,将 modal 保留在另一个组件中,将状态和处理程序保留在另一个组件中......
不用说,在 6000 个标签中,您有 12000 个处理程序和 6000 个状态对象放置在周围......
模态框只有在打开时才会渲染(这确实是 react 的美妙之处),所以这不是问题。在任何情况下,您都需要将模态内容的数据保存在某处(即使您不显示它),所以我并不担心“内存泄漏”(我在示例中没有) .您可能会认为这是浪费内存,但不是泄漏(自动完成元素将被删除 - 按钮和模式也将被删除,并且内存将被释放)。
对话框组件确实会在打开时渲染,但是状态对象,现在我看到有2个,为什么是静态idk时状态中的样式,反正状态和处理程序是创建的每一个人。就我个人而言,我不认为像你那样做这件事的更好方法。如果有这种情况,我会寻找解决方案。在您的示例中有 2 种输入方式,或者在输入中设置 autoFocus
,但如果您在外部单击它就消失了。要么右键单击它,它就会工作......不知道为什么【参考方案2】:
从Autocomplete
中呈现Modal
的问题是事件从Modal
传播到Autocomplete
。特别是,单击和鼠标按下事件均由Autocomplete
处理,其方式会导致您的情况出现问题。这主要是为了在您与Autocomplete
的不同部分交互时将注意力集中在正确的位置。
下面(来自https://github.com/mui-org/material-ui/blob/v4.9.11/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js#L842)是阻碍您前进的Autocomplete
代码部分:
// Prevent input blur when interacting with the combobox
const handleMouseDown = (event) =>
if (event.target.getAttribute('id') !== id)
event.preventDefault();
;
// Focus the input when interacting with the combobox
const handleClick = () =>
inputRef.current.focus();
if (
selectOnFocus &&
firstFocus.current &&
inputRef.current.selectionEnd - inputRef.current.selectionStart === 0
)
inputRef.current.select();
firstFocus.current = false;
;
当鼠标按下事件发生在可聚焦元素上时,默认浏览器行为是让该元素接收焦点,但Autocomplete
的鼠标按下处理程序会调用event.preventDefault()
,这会阻止此默认行为,从而阻止焦点更改从鼠标按下事件(因此焦点停留在Modal
本身,如其蓝色焦点轮廓所示)。但是,您可以使用 tab 键成功地将焦点移动到 Modal 的 TextField,因为没有什么可以阻止焦点更改的机制。
Autocomplete
点击处理程序将焦点放在Autocomplete
的输入上,即使您点击了Autocomplete
的其他部分。当您的Modal
打开时,这样做的效果是,当您单击Modal
时,焦点会短暂移动到Autocomplete
输入元素,但由于其“强制焦点”功能。如果将disableEnforceFocus
属性添加到Modal
、you'll see 中,则当您单击Modal
(例如在TextField 上)时,光标仍保留在Autocomplete
的输入中。
解决方法是确保这两个事件不会传播到Modal
之外。通过为Modal
上的单击和鼠标按下事件调用event.stopPropagation()
,当这些事件在Modal
中发生时,它会阻止执行这两个事件的Autocomplete
功能。
<Modal
onClick=event => event.stopPropagation()
onMouseDown=event => event.stopPropagation()
...
相关回答:How can I create a clickable first option in Material UI Labs Autocomplete
【讨论】:
一个精心编译和解释的答案(y)以上是关于自动完成中的 React Material UI 打开模式失去焦点的主要内容,如果未能解决你的问题,请参考以下文章
当 React Material UI 中的 TextField 中存在值时自定义自动完成 CSS
使用 React Material-UI 自动完成始终显示默认选项
自动完成问题(Material UI + React + Reagent/ClojureScript)
如何在 onChange 后清除 Material-ui 中的自动完成输入?