Dan Zuzevich
Dan Zuzevich

Reputation: 3831

Controlling Input Field Focus With React State and UseEffect

I have a very basic form where I am attempting to control the focus and blur of an input element using state and useEffect. Selecting and deselecting the input via mouse works just fine, but I also have a button that should toggle the focus on/off which is not working properly. When you press the button, the focus never toggles off for some reason.

I must be missing something simple here, but this is driving me crazy. Here is the code below, and a link to a working codepen.

const Focus = () => {
  const [focused, setFocused] = useState(true)
  const inputRef = useRef()

  useEffect(() => {
    if (focused) {
      inputRef.current.focus()
    } else {
      inputRef.current.blur()
    }
  }, [focused])

  return (
    <div>
      <input
        ref={inputRef}
        type="text"
        onBlur={() => setFocused(false)}
        onFocus={() => setFocused(true)}
      />
      <button onClick={() => setFocused(!focused)}>Toggle Focus</button>
    </div>
  )
}

Upvotes: 4

Views: 5443

Answers (1)

Mohammad Faisal
Mohammad Faisal

Reputation: 2363

Commenting off the onBlurHandler solves the issue

 // onBlur={() => setFocused(false)}

when you are calling the setFocused(!focused) then the useEffect is triggered which in turn triggers the onBlur and unnecessarily tries to call setFocus again. That creates an infinite loop of some sort. So just remove that unnecessary onBlur from input because it's not serving any purpose

const Focus = () => {
  const [focused, setFocused] = React.useState(true);
  const inputRef = React.useRef();


  React.useEffect(() => {
    console.log('current ' , focused)
    if (focused) {
      inputRef.current.focus();
    } else {
      inputRef.current.blur();
    }
  }, [focused]);
  
  const toggleFocus = () => {
    setFocused(prev => !prev)
  }
  
  const unFocus = () => {
    setTimeout(() => {
            if (focused) setFocused(false)
        }, 300);
  }
  
  const focus =() => {
    if(!focused)setFocused(true)
  }

  return (
    <div>
      <input
        ref={inputRef}
        type="text"
        onBlur={unFocus}
        onFocus={focus}
      />
      <button onClick={toggleFocus}>Toggle Focus</button>
    </div>
  );
};

ReactDOM.render(<Focus />, document.querySelector('.app'))

The following code solves the issue. I am not sure what is the exact problem but most probably it's the race condition which is handled internally by react

Upvotes: 3

Related Questions