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

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

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

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

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

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

Ответ 1

Вы должны сделать это в 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>

Ответ 2

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

<input autoFocus name=...

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

Ответ 3

Начиная с React 0.15, наиболее сжатый способ:

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

Ответ 4

Сосредоточиться на горе

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

<input type="text" autoFocus />

Интерактивный фокус

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

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

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

    return [ htmlElRef, setFocus ] 
}

Затем используйте его в любом функциональном компоненте.

const FocusDemo = () => {

    const [value, setValue] = useState("")
    const [inputRef, setInputFocus] = useFocus()
    const [btnRef, setBtnFocus] = useFocus()

    return (
        <> {/* React.Fragment */}

            <input
                value={value}
                onChange={(e)=>{
                    setValue(e.target.value)
                    if ( some logic ){ setBtnFocus() }
                }}
                ref={inputRef}
            />

            <button 
               onClick={setInputFocus}
               ref={btnRef}
            >
               Refocus
            </button>

        </>
    )

}

Demo

Реагировать на классы 16.3 + - utilizeFocus

const utilizeFocus = () => {
    const ref = React.createRef()
    const setFocus = () => {ref.current &&  ref.current.focus()}

    return {setFocus, ref} 
}

Затем используйте его в любом компоненте класса

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

  render(){
    return (
      <> {/* React.Fragment */}

          <input
              value={value}
              onChange={(e)=>{
                  setValue(e.target.value)
                  if ( some logic ){ this.BtnFocus.setFocus() }
              }}
              ref={this.input1Focus.ref}
          />

          <button 
             onClick={this.input1Focus.setFocus()}
             ref={this.BtnFocus.ref}
          >
             Refocus
          </button>

      </>
    )
  } 
}

Demo

Ответ 5

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

<input autoFocus type="text" />

Если вам просто нужно знать, где разместить этот код, ответьте в компонентеDidMount().

v014.3

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

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

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

Ответ 6

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

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

  • Сфокусируйте первый элемент ввода (поле), когда компонент монтирует
  • Сфокусируйте первый элемент ввода (поле) с ошибкой (после отправки)

Я использую:

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

Фокусировка первого элемента ввода

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

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

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

Redux Store/State

Мне нужно глобальное состояние, чтобы знать, должен ли я устанавливать фокус и отключать его, когда он был установлен, поэтому я не буду переустанавливать фокус, когда компоненты повторно отображаются (я буду использовать 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. Затем, когда каждый компонент обновляется, он увидит, что он должен проверить, будет ли он получать фокус, и если это произойдет, отправьте событие на reset focus, чтобы другие элементы не нуждались в проверке.

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

Ура!

Ответ 8

В 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>
  );
};

Ответ 9

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

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

Ответ 10

Ref. @Прокомментируйте ответ @Dhiraj; альтернативой является использование функции обратного вызова атрибута ref на визуализируемом элементе (после того, как компонент сначала отобразит):

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

Подробнее

Ответ 11

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

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

Ответ 12

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

Ответ 13

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

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

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

Ответ 14

Вам не нужен 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'));

Ответ 15

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

<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

Ответ 16

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

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

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

Ответ 17

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

Должно быть

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

Ответ 18

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

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

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

Ответ 19

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

Задайте свой текстовый ввод refs

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

Ответ 20

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

У меня был sendingAnswer который 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.
/>

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

Ответ 21

Чтобы переместить фокус на вновь созданный элемент, вы можете сохранить идентификатор элемента в состоянии и использовать его для установки 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 (то есть он не будет продолжать воровать фокус при обновлении других состояний).

Ответ 22

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

componentDidMount() {

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

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

Ответ 23

Также может быть сделано в компонентеDidMount, поскольку он вызывается сразу после рендеринга компонента с помощью

ReactDOM.findDOMNode(this.refs.item).focus();

<input type="text" ref='item'/>

Ответ 24

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

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"/>
  );
}} />