Как установить фокус на поле ввода после рендеринга?


630

Как реагирует установка фокуса на конкретное текстовое поле после рендеринга компонента?

Документация предлагает использовать ссылки, например:

Установите ref="nameInput"в моем поле ввода в функции рендеринга, а затем вызовите:

this.refs.nameInput.getInputDOMNode().focus(); 

Но где мне это назвать? Я пробовал несколько мест, но не могу заставить его работать.

Ответы:


669

Вы должны сделать это в componentDidMountи refs callbackвместо этого. Что-то вроде этого

componentDidMount(){
   this.nameInput.focus(); 
}

class App extends React.Component{
  componentDidMount(){
    this.nameInput.focus();
  }
  render() {
    return(
      <div>
        <input 
          defaultValue="Won't focus" 
        />
        <input 
          ref={(input) => { this.nameInput = input; }} 
          defaultValue="will focus"
        />
      </div>
    );
  }
}
    
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"></script>
<div id="app"></div>


107
Это правильный ответ, но он не работал для меня, так как мой компонент сначала ничего не отображает, пока не нажата другая кнопка. Это означало, что он был уже смонтирован, поэтому мне пришлось добавить this.refs.nameInput.getDOMNode (). Focus (); в componentDidUpdate вместо componentDidMount.
Дейв

9
Почему, когда вызывается element.focus (), он помещает курсор в начало ввода? Я видел эту (что я считаю) ошибку в моем приложении, в chrome, на самом деле в <textarea>, и теперь проверяю ваши демки здесь, это то же самое.
davnicwil

15
Предупреждение: React.findDOMNode устарел. Пожалуйста, используйте вместо этого ReactDOM.findDOMNode из require ('act-dom ').
Андре Пена

5
@HuwDavies Полагаю, вы бы сделали это с помощью атрибута обратного вызова ref для <input>элемента. Нечто подобное<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />
herman

5
Почему мы не просто используем ref = {(input) => {input.focus ()}} ? Это решение отлично работает для меня.
HCLiu

843

Ответ @ Dhiraj правильный, и для удобства вы можете использовать опору autoFocus для автоматической фокусировки при подключении:

<input autoFocus name=...

Обратите внимание, что в jsx это autoFocus(заглавная F) в отличие от простого старого html, который не зависит от регистра.


95
Обратите внимание, что в jsx его автоматический F ocus (заглавная F) отличается от простого старого html, который не учитывает регистр.
prauchfuss

8
Очень хорошо, попал сюда после долгого безрезультатного поиска :) К вашему сведению - я использовал React.DOM.input ({type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target. select ();}})
mlo55

4
Я считаю, что автофокус работает только на рендере первой страницы. См. Codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111: фокус ввода должен быть активирован через 3 секунды.
Эрик Эндрю Льюис

45
+1 за этот метод. Стоит отметить, что здесь используется не просто ненадежный autofocusатрибут HTML5 , а фактически используется focus()при монтировании DOM,react-dom поэтому он достаточно надежен.
Аарон

3
Не только «для удобства», но и если ваш компонент является функциональным компонентом.
Phillyslick

167

Начиная с версии 0.15 , наиболее краткий метод:

<input ref={input => input && input.focus()}/>

5
Это также обрабатывает сценарии вне начального рендеринга, тогда как просто использование автофокуса не делает.
Мэтт

вопрос, когда ввод будет ложным? Я имею в виду выражение внутри функции стрелки.
JaeGeeTee

2
@JaeGeeTee это ноль, пока компонент не смонтирован и / или после того, как он был размонтирован (я точно не помню, в каком случае).
Илья Семенов

12
Единственная проблема с этим заключается в том, что он фокусирует ввод на любом повторном рендере, который может быть нежелателен ..
Ярослав Бенц

Не работает в моем случае (с использованием компонента ввода Ant Design )
vsync

120

Фокус на горе

Если вы просто хотите сфокусировать элемент при его монтировании (первоначальном рендеринге), достаточно использовать атрибут autoFocus.

<input type="text" autoFocus />

Динамический фокус

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

Реакт 16.8 + Функциональный компонент - использовать крючок фокусировки

const FocusDemo = () => {

    const [inputRef, setInputFocus] = useFocus()

    return (
        <> 
            <button onClick={setInputFocus} >
               FOCUS
            </button>
            <input ref={inputRef} />
        </>
    )

}
const useFocus = () => {
    const htmlElRef = useRef(null)
    const setFocus = () => {htmlElRef.current &&  htmlElRef.current.focus()}

    return [ htmlElRef, setFocus ] 
}

Полная демонстрация

Реагируй 16,3 + Класс Компоненты - utilizeFocus

class App extends Component {
  constructor(props){
    super(props)
    this.inputFocus = utilizeFocus()
  }

  render(){
    return (
      <> 
          <button onClick={this.inputFocus.setFocus}>
             FOCUS
          </button>
          <input ref={this.inputFocus.ref}/>
      </>
    )
  } 
}
const utilizeFocus = () => {
    const ref = React.createRef()
    const setFocus = () => {ref.current &&  ref.current.focus()}

    return {setFocus, ref} 
}

Полная демонстрация


2
Этот ответ содержит правильный подход для React Hooks. Супер! Он проверяет не так, как есть в TypeScript, а один (некрасивый) способ заставить его работать: (1) (htmlElRef.current as any).focus()и (2) return {htmlElRef, setFocus}вместо массива.
Ахмед Фасих

@AhmedFasih, я знаю, что вы говорите, но я думаю, что это выходит за рамки этой темы. Если вы возвращаете объект, это усложняет управление именем переменной, что может быть проблемой, если вы хотите использовать useFocusболее одного элемента.
Бен Карп

8
Здесь useFocusнаписано в Typescript. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
Бен Карп

2
Супер-сочно, спасибо !!! Ух ты, я не знал о константных утверждениях (которые у as constтебя есть), очень познавательно!
Ахмед Фасих

@BenCarp Небольшое предложение для крючков, может быть лучше поставить setво вторую позицию, как const [inputRef, setInputFocus] = useFocus(). Это соответствует useState больше. Сначала объект, затем установщик этого объекта
Рубанов

60

Если вы просто хотите сделать автофокус в React, это просто.

<input autoFocus type="text" />

Если вы просто хотите знать, куда поместить этот код, ответ находится в componentDidMount ().

v014.3

componentDidMount() {
    this.refs.linkInput.focus()
}

В большинстве случаев вы можете прикрепить ссылку к узлу DOM и вообще не использовать findDOMNode.

Прочитайте документы API здесь: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode


9
И не забудьте использовать это с большой буквы F! (Примечание для себя и других, а не для ответчика).
2540625

29

В React 16.3 добавлен новый удобный способ справиться с этим путем создания ссылки на конструктор компонента и использования его, как показано ниже:

class MyForm extends Component {
  constructor(props) {
      super(props);

      this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focus(); // one important change here is that we need to access the element via current.
  }

  render() {
    // instead of using arrow function, the created ref can be used directly.
    return(
      <div>
        <input ref={this.textInput} />
      </div>
    );
  }
}

Для более подробной информации, вы можете проверить эту статью в блоге React.

Обновить:

Начиная с React 16.8 , useRefловушка может использоваться в компонентах функций для достижения того же результата:

import React, { useEffect, useRef } from 'react';

const MyForm = () => {
  const textInput = useRef(null);

  useEffect(() => {
    textInput.current.focus();
  }, []);

  return (
    <div>
      <input ref={textInput} />
    </div>
  );
};

26

Я только столкнулся с этой проблемой, и я использую реагировать 15.0.1 15.0.2, и я использую синтаксис ES6 и не совсем получил то, что мне было нужно из других ответов, так как v.15 упала несколько недель назад и некоторые this.refsсвойства были объявлены устаревшими и удалены .

В общем то, что мне было нужно, было:

  1. Фокус первого элемента ввода (поля), когда компонент монтируется
  2. Фокус первого элемента ввода (поля) с ошибкой (после отправки)

Я использую:

  • Реагировать Контейнер / Презентация Компонент
  • Redux
  • Реагировать-маршрутизатор

Сосредоточиться на первом элементе ввода

Я использовал autoFocus={true}первый <input />на странице, чтобы, когда компонент монтировался, он получал фокус.

Сосредоточить первый элемент ввода с ошибкой

Это заняло больше времени и было более запутанным. Я храню код, который не имеет отношения к решению для краткости.

Redux Store / Государство

Мне нужно глобальное состояние, чтобы знать, должен ли я установить фокус и отключить его, когда он был установлен, поэтому я не буду переустанавливать фокус при повторной визуализации компонентов (я буду использовать componentDidUpdate()для проверки установки фокуса. )

Это может быть разработано так, как вы считаете подходящим для вашего приложения.

{
    form: {
        resetFocus: false,
    }
}

Контейнер Компонент

Компонент должен иметь установленное resetfocusсвойство и callBack, чтобы очистить свойство, если оно в конечном итоге устанавливает фокус на себя.

Также обратите внимание, что я организовал своих создателей Action в отдельные файлы, в основном из-за того, что мой проект довольно большой, и я хотел разбить их на более управляемые куски.

import { connect } from 'react-redux';
import MyField from '../presentation/MyField';
import ActionCreator from '../actions/action-creators';

function mapStateToProps(state) {
    return {
        resetFocus: state.form.resetFocus
    }
}

function mapDispatchToProps(dispatch) {
    return {
        clearResetFocus() {
            dispatch(ActionCreator.clearResetFocus());
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(MyField);

Компонент презентации

import React, { PropTypes } form 'react';

export default class MyField extends React.Component {
    // don't forget to .bind(this)
    constructor(props) {
        super(props);
        this._handleRef = this._handleRef.bind(this);
    }

    // This is not called on the initial render so
    // this._input will be set before this get called
    componentDidUpdate() {
        if(!this.props.resetFocus) {
            return false;
        }

        if(this.shouldfocus()) {
            this._input.focus();
            this.props.clearResetFocus();
        }
    }

    // When the component mounts, it will save a 
    // reference to itself as _input, which we'll
    // be able to call in subsequent componentDidUpdate()
    // calls if we need to set focus.
    _handleRef(c) {
        this._input = c;
    }

    // Whatever logic you need to determine if this
    // component should get focus
    shouldFocus() {
        // ...
    }

    // pass the _handleRef callback so we can access 
    // a reference of this element in other component methods
    render() {
        return (
            <input ref={this._handleRef} type="text" />
        );
    }
}

Myfield.propTypes = {
    clearResetFocus: PropTypes.func,
    resetFocus: PropTypes.bool
}

обзор

Общая идея заключается в том, что каждое поле формы, которое может иметь ошибку и быть сфокусированным, должно проверять себя и не должно ли оно сосредоточиться на себе.

Есть бизнес-логика, которая должна произойти, чтобы определить, является ли данное поле правильным полем для фокусировки. Это не показано, потому что это будет зависеть от конкретного приложения.

Когда форма отправлена, это событие должно установить флаг глобального фокуса resetFocusв true. Затем, когда каждый компонент обновляется сам, он увидит, что он должен проверить, получает ли он фокус, и если это так, отправляет событие, чтобы сбросить фокус, чтобы другие элементы не должны были продолжать проверку.

править Как примечание стороны, у меня был бизнес - логика в файле «утилиты» , и я только что экспортировал метод и назвал его в пределах каждогоshouldfocus() метода.

Ура!


25

В документации React есть раздел для этого. https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute

 render: function() {
  return (
    <TextInput
      ref={function(input) {
        if (input != null) {
          input.focus();
        }
      }} />
    );
  },

1
Я думаю, что это хороший способ сделать это для этого конкретного сценария.
fabio.sussetto

Мне не нужно было autofocusмонтировать, я просто искал элемент, чтобы оставаться сфокусированным при вводе значения. Это отлично сработало для этого сценария. (используя реакцию 15)
Мэтт Паррилья

13

Это уже не лучший ответ. Начиная с версии v0.13 this.refsможет быть недоступно до запуска ПОСЛЕ componentDidMount(), в некоторых странных случаях.

Просто добавьте autoFocusтег в поле ввода, как показано выше в FakeRainBrigand.


4
Несколько <input autofocus>полей не будут вести себя хорошо
ᆼ ᆺ ᆼ

4
Конечно, нет. Только один фокус на странице. Если у вас есть несколько автофокусов, вы должны проверить свой код и намерения.
GAEfan

2
@ Вопрос Дэйва был об установке фокуса <input>после рендера
ᆼ ᆺ ᆼ

1
На автофокусе есть ли способ заставить клавиатуру iOS открыться?
Реми Стуре

1
@RemiSture те же вопросы. У кого-нибудь есть решение этой проблемы?
Nam Lê Quý

12

Ссылка @ Комментарий Дэйва к ответу @ Дхираджа; альтернатива состоит в том, чтобы использовать функцию обратного вызова атрибута ref для визуализируемого элемента (после первого рендеринга компонента):

<input ref={ function(component){ React.findDOMNode(component).focus();} } />

Больше информации


Когда я попробовал это, я получил:Uncaught TypeError: Cannot read property 'focus' of null
reectrix

1
Вы должны проверить параметр на null, он будет нулевым, когда компонент не смонтирован. Так просто component && React.findDomNode.... Подробнее об этом здесь: facebook.github.io/react/docs/...
Per Wiklander

12

Это правильный способ, как с автофокусом. Когда вы используете callback вместо строки в качестве значения ref, он вызывается автоматически. Вы получили свой реф, чем без необходимости касаться DOM, используяgetDOMNode

render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},

2
как насчет контролируемой формы?
пиксель 67

@ pixel67 Также. Вы можете установить ссылку на элементы, но и компоненты. Но вы должны знать об этом при работе с ним. Таким образом, вы не будете пытаться получить доступ к .value ввода, если вы установите ссылку на React.Component, который обернет ввод html.
Павел Хасала

10

Обратите внимание, что ни один из этих ответов не работал для меня с компонентом TextField для материала и пользовательского интерфейса . Per Как установить фокус на materialUI TextField? Мне пришлось перепрыгнуть через несколько обручей, чтобы заставить это работать:

const focusUsernameInputField = input => {
  if (input) {
    setTimeout(() => {input.focus()}, 100);
  }
};

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);

2
Похоже, если ваш компонент анимируется, вызов focus()должен быть отложен до конца анимации.
Адриенденат

9

Вы можете поместить этот метод в функцию рендера. Или внутри метода жизненного цикла,componentDidUpdate


1
componentDidUpdate - это то, что сработало для моего случая. Мне нужно было установить фокус на определенной кнопке после вызова рендера.
FariaC

8

Тебе не нужно getInputDOMNode?? в этом случае...

Просто получите refи focus()это, когда компонент монтируется - componentDidMount ...

import React from 'react';
import { render } from 'react-dom';

class myApp extends React.Component {

  componentDidMount() {
    this.nameInput.focus();
  }

  render() {
    return(
      <div>
        <input ref={input => { this.nameInput = input; }} />
      </div>
    );
  }

}

ReactDOM.render(<myApp />, document.getElementById('root'));

5

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

<input autoFocus onFocus={this.setCaretToEnd} value={this.state.editTodo.value} onDoubleClick={this.updateTodoItem} />

ПРИМЕЧАНИЕ. Чтобы устранить проблему, когда React размещает курсор в начале текста, используйте этот метод:

setCaretToEnd(event) {
    var originalText = event.target.value;
    event.target.value = '';
    event.target.value = originalText;
}

Найдено здесь: https://coderwall.com/p/0iz_zq/how-to-put-focus-at-the-end-of-an-input-with-react-js


3

У меня та же проблема, но у меня тоже есть анимация, поэтому мой коллега предлагает использовать window.requestAnimationFrame

это атрибут ref моего элемента:

ref={(input) => {input && window.requestAnimationFrame(()=>{input.focus()})}}

2

Предупреждение: ReactDOMComponent: не обращайтесь к .getDOMNode () узла DOM; вместо этого используйте узел напрямую. Этот DOM-узел был обработан App.

Должно быть

componentDidMount: function () {
  this.refs.nameInput.focus();
}

2

Самый простой ответ - добавить ref = "some name" во входной текстовый элемент и вызвать функцию ниже.

componentDidMount(){
   this.refs.field_name.focus();
}
// here field_name is ref name.

<input type="text" ref="field_name" />

2

Чтобы переместить фокус на вновь созданный элемент, вы можете сохранить идентификатор элемента в состоянии и использовать его для установки autoFocus. например

export default class DefaultRolesPage extends React.Component {

    addRole = ev => {
        ev.preventDefault();
        const roleKey = this.roleKey++;
        this::updateState({
            focus: {$set: roleKey},
            formData: {
                roles: {
                    $push: [{
                        id: null,
                        name: '',
                        permissions: new Set(),
                        key: roleKey,
                    }]
                }
            }
        })
    }

    render() {
        const {formData} = this.state;

        return (
            <GridForm onSubmit={this.submit}>
                {formData.roles.map((role, idx) => (
                    <GridSection key={role.key}>
                        <GridRow>
                            <GridCol>
                                <label>Role</label>
                                <TextBox value={role.name} onChange={this.roleName(idx)} autoFocus={role.key === this.state.focus}/>
                            </GridCol>
                        </GridRow>
                    </GridSection>
                ))}
            </GridForm>
        )
    }
}

Таким образом, ни одно из текстовых полей не фокусируется на загрузке страницы (как я хочу), но когда вы нажимаете кнопку «Добавить», чтобы создать новую запись, тогда эта новая запись получает фокус.

Так autoFocusкак не «запускается» снова, пока компонент не перемонтируется, мне не нужно беспокоиться об отключении this.state.focus(т.е. он не будет продолжать воровать фокус при обновлении других состояний).


1

Прочитал почти весь ответ, но не увидел getRenderedComponent().props.input

Установите текстовые ссылки для ввода

this.refs.username.getRenderedComponent().props.input.onChange('');


Пожалуйста, уточните свой ответ в контексте их кода.
Джимми Смит

1

Попробовав множество вариантов, описанных выше, безуспешно, я обнаружил, что все было так, как было, disablingа затем enablingвход, из-за которого фокус был потерян.

У меня была опора, sendingAnswerкоторая отключала бы вход, пока я опрашивал бэкэнд.

<Input
  autoFocus={question}
  placeholder={
    gettingQuestion ? 'Loading...' : 'Type your answer here...'
  }
  value={answer}
  onChange={event => dispatch(updateAnswer(event.target.value))}
  type="text"
  autocomplete="off"
  name="answer"
  // disabled={sendingAnswer} <-- Causing focus to be lost.
/>

Как только я удалил отключенную опору, все снова заработало.


0

Обновленную версию вы можете проверить здесь

componentDidMount() {

    // Focus to the input as html5 autofocus
    this.inputRef.focus();

}
render() {
    return <input type="text" ref={(input) => { this.inputRef = input }} />
})

0

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

export default ({ Content }) => {
  return (
  <div className="container-fluid main_container">
    <div className="row">
      <div className="col-sm-12 h-100">
        <Content />                                 // I rendered my inputs here
      </div>
    </div>
  </div>
  );
}

Вот как я назвал вышеупомянутый компонент:

<Component Content={() => {
  return (
    <input type="text"/>
  );
}} />

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.