Управление фокусом поля ввода с помощью React State и UseEffect

avatar
Dan Zuzevich
1 июля 2021 в 16:16
749
1
1

У меня есть очень простая форма, в которой я пытаюсь управлять фокусом и размытием элемента ввода, используя состояние и useEffect. Выбор и отмена выбора ввода с помощью мыши работает нормально, но у меня также есть кнопка, которая должна включать/выключать фокус, которая не работает должным образом. Когда вы нажимаете кнопку, фокус почему-то никогда не отключается.

Должно быть, я упускаю здесь что-то простое, но это сводит меня с ума. Вот приведенный ниже код и ссылка на рабочий 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>
  )
}
Источник
Keith
1 июля 2021 в 16:19
0

Входные данные имеют атрибут autofocus, просто используйте его.

Dan Zuzevich
1 июля 2021 в 16:20
0

Это не то, что я ищу. Я хочу иметь возможность использовать кнопку для включения и выключения фокуса по моему выбору. Не работает только кнопка. Он делает что-то странное, чего я смущаюсь, но не могу найти.

Ответы (1)

avatar
Mohammad Faisal
1 июля 2021 в 16:23
1

Комментирование onBlurHandler решает проблему

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

когда вы вызываете setFocused(!focused), то срабатывает useEffect, который, в свою очередь, запускает onBlur и без необходимости пытается снова вызвать setFocus. Это создает своего рода бесконечный цикл. Так что просто удалите этот ненужный onBlur из ввода, потому что он не служит никакой цели

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'))

Следующий код решает проблему. Я не уверен, в чем именно проблема, но, скорее всего, это состояние гонки, которое обрабатывается внутри react

Dan Zuzevich
1 июля 2021 в 16:27
0

Нет бесконечного цикла, я проверяю в консоли, сколько раз компонент перерисовывается. Кроме того, с вашим решением, которое теперь вызывает еще одну ошибку.

Mohammad Faisal
1 июля 2021 в 16:28
0

можно узнать что это за баг?

Dan Zuzevich
1 июля 2021 в 16:29
0

Если вы перейдете к этому codepen и закомментируете onBlur. Попробуйте обновить страницу и 1.) расфокусировать ввод, щелкнув в любом месте страницы 2.) нажать кнопку переключения фокуса. Ввод должен снова стать сфокусированным, но для фокусировки требуется два щелчка кнопки.

Tom
1 июля 2021 в 16:29
1

@DanZuzevich Я все еще думаю, что Мохаммад что-то здесь понимает. Ясно: нажатие кнопки размывает поле при обычной работе DOM, независимо от того, что делает React. Итак, даже если это не бесконечный цикл, у вас есть случай, когда сработают два конфликтующих обновления: сработает размытие поля, а затем сработает щелчок. Я думаю, что это не настоящее состояние гонки, но оно близко.

Dan Zuzevich
1 июля 2021 в 16:33
0

Я знаю, что вы говорите о ненужном вызове setFocus. Если вы посмотрите на вызов useEffect, при начальном монтировании он вызовет фокус и вызовет функцию onFocus, которая устанавливает focus = true. В этом нет необходимости, потому что это уже правда. Так что да, я думаю, что то же самое происходит с функцией onBlur?

Dan Zuzevich
1 июля 2021 в 16:35
0

НАСА может отправить роботов на Марс, а я не могу понять, как правильно сфокусировать/размыть свою форму. Фу.

Tom
1 июля 2021 в 16:38
0

Я думаю, что это больше, чем просто "ненужно" - я думаю, что это причина ошибки. Я понимаю, почему решение Мохаммеда не совсем то, что вам нужно, но я думаю, что он нашел причину проблемы. Сценарий, который вам нужно тщательно отладить: поле имеет фокус, и пользователь нажимает кнопку.

Mohammad Faisal
1 июля 2021 в 16:46
0

Я думаю, что это проблема состояния гонки. Я не могу это доказать, но могу предложить альтернативное решение, в котором мы попытаемся установить состояние после задержки в 300 мс. Что также решает проблему. Проверьте мой обновленный ответ

Dan Zuzevich
1 июля 2021 в 16:47
0

Хорошо, спасибо, ребята. Сейчас у меня перерыв на обед, но я вернусь к этому ответу и проверю его. Я проголосую, если это покажется хорошим решением. Благодарю вас!