skip to Main Content

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


  1. Chosen as BEST ANSWER

    fixed this by adding timeout like this

      useImperativeHandle(ref, () => ({
        focusInput: () => {
          setTimeout(()=>{
          focusableElements[0].value = '1'
          focusableElements[0].focus()
        },100)
        },
      }))
    

  2. 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:

    focusInput: () => {
      console.log(focusableElements[0])
      focusableElements[0].focus()
      focusableElements[0].value = '1'
    },
    

    This way, the input element will be focused first and then its value will be set to ‘1’.

    Login or Signup to reply.
  3. Add a console.log("onTabChange in parent") in onTabChange in your parent component.

    Your should see it been called before your console.log(focusableElements[0]), and not again, not after.

    For 2 reasons :

    • You put setFocusableElements(messageInput) in a useEffect, because ref.current need to be rendered.
      But it will be called after the onTabChange which triggered the all thing in the first place.
    • The useEffect will call setFocusableElements which will trigger a re-render of the childElement.
      The useImperativeHandle will be updated on this re-render with the FocusableElements,
      but changing useImperativeHandle will not trigger a re-render of the parent using the ref.

    Solution :

    The focus() should be place in a useEffect.

    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 :

    // In the parent component :
    const parent = () => {
       const [willFocus, setWillFocus] = useState(false);
       const onTabChange = activeKey => {
          if (activeKey === 1 && chatThreadRef.current) {
            setWillFocus(true)
          }
       }
       return <Child willFocus={willFocus} />
    
    // In the child component :
    const parent = ({willFocus}) => {
       const ref = useRef(null);
       useEffect(() => {
          willFocus && ref.current.focus();
       }, [willFocus]);
    
       return <input ref={ref} />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search