我们有一个包含 3 个部分的表格,并且要求用户按照下图所示的特定顺序填写此表格(1. 来自 Bin,2. Item,3. To Bin)。在一个部分中输入所需信息后,下一个部分应自动出现,并将焦点放在第一个输入上。这 3 个部分只是 1 个 React-hook-form 的一部分。
但是,我们注意到,当我们更改部分时,react-hook-form 的
setFocus
函数不会将焦点设置在第一个输入上,并且我们似乎无法确定这是为什么或如何使其这样做。我见过一些 setTimeout hack,但这似乎不是正确的方法。
这是一个简单的 codesandbox,我们放在一起来复制这个问题。
对此的任何帮助将不胜感激!
这里有一个使用 TypeScript、React 和 MaterialUI's Accordion 的解决方案。打开手风琴元素会将焦点转移到其输入元素。
@Igor Gonak 是正确的;根本原因是
focus()
方法对未显示的 DOM 元素不执行任何操作。您必须延迟调用 focus()
,直到手风琴组件呈现之后。您可以使用 setTimout()
(更简单,请参阅下面的输入一)或使用触发渲染后效果的状态标志(输入二)来做到这一点。
import "./styles.css";
import { Accordion, AccordionDetails, AccordionSummary } from "@mui/material";
import { ExpandMore } from "@mui/icons-material";
import { SyntheticEvent, useEffect, useRef, useState } from "react";
export default function App() {
const ref1 = useRef<HTMLInputElement>(null);
const ref2 = useRef<HTMLInputElement>(null);
const [focus, setFocus] = useState(false);
useEffect(() => {
if (focus) {
setFocus(false);
ref2.current?.focus();
}
}, [focus]);
return (
<div className="App">
<Accordion
onChange={(_: SyntheticEvent, expanded: boolean) => {
if (expanded) {
setTimeout(() => ref1.current?.focus(), 100);
}
}}
>
<AccordionSummary expandIcon={<ExpandMore />}>One</AccordionSummary>
<AccordionDetails>
<input ref={ref1} placeholder="Input one" />
</AccordionDetails>
</Accordion>
<Accordion
onChange={(_: SyntheticEvent, expanded: boolean) => {
if (expanded) {
setFocus(true);
}
}}
>
<AccordionSummary expandIcon={<ExpandMore />}>Two</AccordionSummary>
<AccordionDetails>
<input ref={ref2} placeholder="Input two" />
</AccordionDetails>
</Accordion>
<input placeholder="Input three" />
</div>
);
}
我只能为您指出正确的方向 - 这个问题是因为手风琴项目被隐藏了。如果我通过设置 activeKey 来展开它们,然后浏览输入,它就可以工作。 Accordion.Body 折叠时显示=无,并且您无法聚焦隐藏元素。
当我在 useEffect 中设置焦点时,在 activeIndex 更改后,我认为它可以解决这个问题,但这并没有解决问题:
useEffect(() => {
if (activeSection === 1) {
setFocus("itemID");
}
if (activeSection === 2) {
setFocus("binTo");
}
}, [activeSection, setFocus]);
也没有重新渲染,这可能会导致失去焦点。
我的猜测是手风琴组件本身尚未完成渲染,因此在手风琴完全渲染之前调用了 setFocus 。添加超时可能会有所帮助。
useEffect(() => {
if (isAccordionOpen) {
setTimeout(() => {
setFocus("imageUrl");
}, 50);
}
}, [isAccordionOpen]);