无法在React中聚焦输入。

huangapple go评论61阅读模式
英文:

Not able to focus input in react

问题

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 chatThreadRef = 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 ref={chatThreadRef} /> },
      ]}
      onChange={onTabChange}
    />
  );
};

Child component

const ChildComponent = forwardRef((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 in the parent component, the console.log(focusableElements[0]) statement in the code prints the input element, and you're also able to set the value to the input, but it's not getting focused.

英文:

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=()=&gt;{
const threadRef=useRef()
() =&gt; {
  const onTabChange = activeKey =&gt; {
    if (activeKey === 1 &amp;&amp; chatThreadRef.current) {
      chatThreadRef.current.focusInput()
    }
  }
  return (
    &lt;Tabs
      items={[
        { label: &#39;tab 1&#39;, key: 0, children: &lt;&gt;tab 1&lt;/&gt; },
        { label: &#39;tab 2&#39;, key: 1, children: &lt;ChildComponent/&gt; },
      ]}
      onChange={onTabChange}
    /&gt;
  )
}
}

Child component

const ChildComponent = forwardRed((props,ref) =&gt; {
  const [focusableElements, setFocusableElements] = useState([])
  const wrapperRef = useRef(null)
  useEffect(() =&gt; {
    const messageInput = wrapperRef.current.querySelector(&#39;input&#39;)
    setFocusableElements(messageInput)
  }, [])

  useImperativeHandle(ref, () =&gt; ({
    focusInput: () =&gt; {
      console.log(focusableElements[0])
      focusableElements[0].value = &#39;1&#39;
      focusableElements[0].focus()
    },
  }))
  return (
    &lt;div ref={wrapperRef}&gt;
      &lt;Input className=&quot;input&quot; /&gt;
    &lt;/div&gt;
  )
})

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.

答案1

得分: 0

看起来你的代码中可能有一个小错误。在调用focus()之前,你应该将focusableElements[0]输入元素的value属性设置为'1',而不是之后。

尝试将你在子组件中的focusInput函数改成:

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

这样,输入元素将首先获得焦点,然后其值将被设置为'1'。

英文:

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: () =&gt; {
  console.log(focusableElements[0])
  focusableElements[0].focus()
  focusableElements[0].value = &#39;1&#39;
},

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

答案2

得分: 0

在你的父组件的onTabChange中添加console.log(&quot;onTabChange in parent&quot;)

你应该看到它在你的console.log(focusableElements[0])之前被调用,而且不会再次调用,也不会在之后调用。

有两个原因:

  • 你将setFocusableElements(messageInput)放在useEffect中,因为ref.current需要被渲染。但它会在首次触发整个过程的onTabChange之后调用。
  • useEffect将调用setFocusableElements,这将触发对childElement的重新渲染。useImperativeHandle将在此重新渲染中使用FocusableElements进行更新,但更改useImperativeHandle 不会 触发使用ref的父组件的重新渲染。

解决方案:

focus() 应该放在 useEffect 中。

但再说一遍,如果你将这个调用放在父组件中,它将不起作用。子组件的重新渲染不会触发其父组件的重新渲染。最好直接在子组件上放置焦点。

我们这里没有关于你为什么要这样构建的背景信息,但如果你绝对需要父组件控制焦点,请改用布尔型的props:

// 在父组件中:
const parent = () =&gt; {
   const [willFocus, setWillFocus] = useState(false);
   const onTabChange = activeKey =&gt; {
      if (activeKey === 1 &amp;&amp; chatThreadRef.current) {
        setWillFocus(true)
      }
   }
   return &lt;Child willFocus={willFocus} /&gt;
// 在子组件中:
const parent = ({willFocus}) =&gt; {
   const ref = useRef(null);
   useEffect(() =&gt; {
      willFocus &amp;&amp; ref.current.focus();
   }, [willFocus]);

   return &lt;input ref={ref} /&gt;
英文:

Add a console.log(&quot;onTabChange in parent&quot;) 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 = () =&gt; {
   const [willFocus, setWillFocus] = useState(false);
   const onTabChange = activeKey =&gt; {
      if (activeKey === 1 &amp;&amp; chatThreadRef.current) {
        setWillFocus(true)
      }
   }
   return &lt;Child willFocus={willFocus} /&gt;
// In the child component :
const parent = ({willFocus}) =&gt; {
   const ref = useRef(null);
   useEffect(() =&gt; {
      willFocus &amp;&amp; ref.current.focus();
   }, [willFocus]);

   return &lt;input ref={ref} /&gt;

答案3

得分: 0

I fixed this by adding a timeout like this:

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

fixed this by adding timeout like this

  useImperativeHandle(ref, () =&gt; ({
    focusInput: () =&gt; {
      setTimeout(()=&gt;{
      focusableElements[0].value = &#39;1&#39;
      focusableElements[0].focus()
    },100)
    },
  }))

huangapple
  • 本文由 发表于 2023年4月17日 15:12:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76032508.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定