我尝试在用户点击Antd标签时(也就是输入框可见时)聚焦Antd标签内的输入框。我使用useImperativeHandle钩子来实现这一点。
父组件
const Parent=()=>{
const threadRef=useRef()
() => {
const onTabChange = activeKey => {
if (activeKey === 1 && chatThreadRef.current) {
chatThreadRef.current.focusInput()
}
}
return (
<Tabs
items={[
{ label: 'tab 1', key: 0, children: <>tab 1</> },
{ label: 'tab 2', key: 1, children: <ChildComponent/> },
]}
onChange={onTabChange}
/>
)
}
}
子组件
const ChildComponent = forwardRed((props,ref) => {
const [focusableElements, setFocusableElements] = useState([])
const wrapperRef = useRef(null)
useEffect(() => {
const messageInput = wrapperRef.current.querySelector('input')
setFocusableElements(messageInput)
}, [])
useImperativeHandle(ref, () => ({
focusInput: () => {
console.log(focusableElements[0])
focusableElements[0].value = '1'
focusableElements[0].focus()
},
}))
return (
<div ref={wrapperRef}>
<Input className="input" />
</div>
)
})
无论何时调用focusInput,代码中的父组件console.log(focusableElements[0])
语句都会打印input元素,我也可以将值设置为input,但它不会得到焦点。
2条答案
按热度按时间nlejzf6q1#
看起来你的代码中可能有一个小错误。在调用focus()之前,不要将focusableElements[0]输入元素的value属性设置为“1”,而应该在调用focus()之后设置它。
尝试将子组件中的focusInput函数更改为:
这样,输入元素将首先被聚焦,然后其值将被设置为“1”。
vuv7lop32#
在父组件的
onTabChange
中添加console.log("onTabChange in parent")
。你应该看到它在你的
console.log(focusableElements[0])
之前被称为**,而不是之后。原因有二:
setFocusableElements(messageInput)
放在useEffect
中,因为ref.current
需要被渲染。但是它会在onTabChange
之后被调用,而onTabChange
首先触发了all thing。useEffect
将调用setFocusableElements
,这将触发childElement的重新渲染。useImperativeHandle
将在此重新渲染时使用FocusableElements
进行更新,但更改useImperativeHandle
不会触发使用ref重新渲染父元素。解决方案:
focus()
应放置在useEffect
中。但是,如果你把这个调用放在父对象中,它将不起作用。子对象的重新渲染不会触发父对象的重新渲染。最好直接把焦点放在子对象上。
我们这里没有关于为什么要这样构建的上下文,但是如果你绝对需要由父对象控制焦点,请使用布尔props: