You need useLayoutEffect
:
const toggleInputCardName = () => {
setInputCardNameVisibity(!isInputCardName)
}
React.useLayoutEffect(() => {
if (!inputRef.current) return
inputRef.current.focus()
inputRef.current.select()
}, [isInputCardName])
The reason it doesn't work in the handler or in a plain useEffect
is that they are executing before the input is actually written to the DOM. State changes in react are flushed later, and don't happen immediately. useLayoutEffect
waits until the DOM change is committed.
Another way of doing it is by wrapping it in a 0
second timeout. This looks hackier than it is, as a timer with 0 as the time to wait, will just wait until the current call stack is finished before executing.
const toggleInputCardName = () => {
setInputCardNameVisibity(!isInputCardName)
setTimeout(() => {
if (inputRef.current) {
console.log('YES inputRef.current', inputRef.current)
inputRef.current.focus()
inputRef.current.select()
} else {
console.log('NO inputRef.current', inputRef.current, isInputCardName)
}
}, 0)
}
But I'd probably useLayoutEffect
.
@OneQ answer of using the focus attribute also makes a lot of sense and reduces noise.