I’m trying to focus an input box that is inside an Antd tab whenever the user clicks this tab (i.e., whenever the input box is visible). I’m using the useImperativeHandle hook to achieve this.
parent component
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}
/>
)
}
}
Child component
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>
)
})
Whenever the focusInput is called the in the parent component console.log(focusableElements[0])
statement in the code prints the input element and i’m also able to set the value to the input but its not getting focussed.
3
Answers
fixed this by adding timeout like this
It looks like there might be a small mistake in your code. Instead of setting the value property of the focusableElements[0] input element to ‘1’ before calling focus(), you should set it after calling focus().
Try changing your focusInput function in the child component to:
This way, the input element will be focused first and then its value will be set to ‘1’.
Add a
console.log("onTabChange in parent")
inonTabChange
in your parent component.Your should see it been called before your
console.log(focusableElements[0])
, and not again, not after.For 2 reasons :
setFocusableElements(messageInput)
in auseEffect
, becauseref.current
need to be rendered.But it will be called after the
onTabChange
which triggered the all thing in the first place.useEffect
will callsetFocusableElements
which will trigger a re-render of the childElement.The
useImperativeHandle
will be updated on this re-render with theFocusableElements
,but changing
useImperativeHandle
will not trigger a re-render of the parent using the ref.Solution :
The
focus()
should be place in auseEffect
.But then again, if you place this call in the parent, it will not work.
A child re-render will not trigger is parent re-render.
Best if you place directly the focus on the child.
We don’t have the context here about why you build that this way, but if you absolutely need the focus to be controlled by the parent, use a boolean props instead :