英文:
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=()=>{
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.
答案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: () => {
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'.
答案2
得分: 0
在你的父组件的onTabChange
中添加console.log("onTabChange in parent")
你应该看到它在你的console.log(focusableElements[0])
之前被调用,而且不会再次调用,也不会在之后调用。
有两个原因:
- 你将
setFocusableElements(messageInput)
放在useEffect
中,因为ref.current
需要被渲染。但它会在首次触发整个过程的onTabChange
之后调用。 useEffect
将调用setFocusableElements
,这将触发对childElement
的重新渲染。useImperativeHandle
将在此重新渲染中使用FocusableElements
进行更新,但更改useImperativeHandle
不会 触发使用ref的父组件的重新渲染。
解决方案:
focus()
应该放在 useEffect
中。
但再说一遍,如果你将这个调用放在父组件中,它将不起作用。子组件的重新渲染不会触发其父组件的重新渲染。最好直接在子组件上放置焦点。
我们这里没有关于你为什么要这样构建的背景信息,但如果你绝对需要父组件控制焦点,请改用布尔型的props:
// 在父组件中:
const parent = () => {
const [willFocus, setWillFocus] = useState(false);
const onTabChange = activeKey => {
if (activeKey === 1 && chatThreadRef.current) {
setWillFocus(true)
}
}
return <Child willFocus={willFocus} />
// 在子组件中:
const parent = ({willFocus}) => {
const ref = useRef(null);
useEffect(() => {
willFocus && ref.current.focus();
}, [willFocus]);
return <input ref={ref} />
英文:
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 auseEffect
, becauseref.current
need to be rendered.
But it will be called after theonTabChange
which triggered the all thing in the first place. - The
useEffect
will callsetFocusableElements
which will trigger a re-render of the childElement.
TheuseImperativeHandle
will be updated on this re-render with theFocusableElements
,
but changinguseImperativeHandle
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} />
答案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, () => ({
focusInput: () => {
setTimeout(()=>{
focusableElements[0].value = '1'
focusableElements[0].focus()
},100)
},
}))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论