Я пытаюсь сфокусировать поле ввода, которое находится внутри вкладки 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.info(focusableElements[0])
focusableElements[0].value = '1'
focusableElements[0].focus()
},
}))
return (
<div ref = {wrapperRef}>
<Input className = "input" />
</div>
)
})
Всякий раз, когда focusInput вызывается в родительском компоненте, оператор console.info(focusableElements[0])
в коде печатает элемент ввода, и я также могу установить значение для ввода, но он не фокусируется.
Похоже, в вашем коде может быть небольшая ошибка. Вместо того, чтобы устанавливать для свойства value элемента ввода focusableElements[0] значение '1' перед вызовом focus(), вы должны установить его после вызова focus().
Попробуйте изменить функцию focusInput в дочернем компоненте на:
focusInput: () => {
console.info(focusableElements[0])
focusableElements[0].focus()
focusableElements[0].value = '1'
},
Таким образом, элемент ввода будет сначала сфокусирован, а затем его значение будет установлено на «1».
Добавьте console.info("onTabChange in parent")
в onTabChange
в родительский компонент.
Вы должны увидеть, что это было вызвано до вашего console.info(focusableElements[0])
, а не снова, не после.
По 2 причинам:
setFocusableElements(messageInput)
в useEffect
, потому что ref.current
нужно отрендерить.
Но он будет называться после onTabChange
, который в первую очередь вызвал все это.useEffect
вызовет setFocusableElements
, что вызовет повторную визуализацию дочернего элемента.
useImperativeHandle
будет обновлен при этом повторном рендеринге с FocusableElements
,
но изменение useImperativeHandle
не приведет к повторному рендерингу родителя с использованием ссылки.focus()
должен стоять в useEffect
.
Но опять же, если вы поместите этот вызов в родителя, он не будет работать. Дочерний повторный рендеринг не будет запускаться, если родительский повторный рендеринг. Лучше всего, если вы сосредоточите внимание непосредственно на ребенке.
У нас нет здесь контекста о том, почему вы строите это таким образом, но если вам абсолютно необходимо, чтобы фокус контролировался родителем, вместо этого используйте логические реквизиты:
// 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} />
исправил это, добавив время ожидания, подобное этому
useImperativeHandle(ref, () => ({
focusInput: () => {
setTimeout(()=>{
focusableElements[0].value = '1'
focusableElements[0].focus()
},100)
},
}))
вы не передаете ссылку в ChildComponent (и у вас есть опечатка в
forwardRef
). Кроме того, почему вы используетеref
, а затем запрашиваете его? Просто примените ссылку к фактическому элементу, на который вы хотите ссылаться. Вы также можете просто применить свойствоautoFocus
к входу.