Jak ustawić fokus na polu wejściowym po renderowaniu?

629

Jak zareaguje ustawienie fokusu na określonym polu tekstowym po renderowaniu komponentu?

Dokumentacja wydaje się sugerować użycie referencji, np .:

Ustaw ref="nameInput"w moim polu wejściowym w funkcji renderowania, a następnie wywołaj:

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

Ale gdzie mam to nazwać? Próbowałem już w kilku miejscach, ale nie mogę tego zrobić.

Dave
źródło

Odpowiedzi:

668

Należy to zrobić w componentDidMounta refs callbackzamiast. Coś takiego

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>

Dhiraj
źródło
107
To poprawna odpowiedź, ale nie zadziałało, ponieważ mój komponent najpierw nic nie renderuje, dopóki nie kliknie innego przycisku. Oznaczało to, że został już zamontowany, więc musiałem dodać this.refs.nameInput.getDOMNode (). Focus (); w componentDidUpdate zamiast componentDidMount.
Dave
9
Dlaczego, gdy wywoływana jest funkcja element.focus (), umieszcza kursor na początku danych wejściowych? Widziałem ten błąd (co uważam za) w mojej aplikacji, w chrome, właściwie w <textarea>, a teraz sprawdzanie dem tutaj tutaj jest takie samo.
davnicwil
15
Ostrzeżenie: React.findDOMNode jest nieaktualny. Zamiast tego użyj ReactDOM.findDOMNode z request („domagaj się”).
André Pena
5
@HuwDavies Wydaje mi się, że zrobiłbyś to za pomocą referencyjnego atrybutu wywołania zwrotnego dla <input>elementu. Coś w stylu<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />
herman
5
Dlaczego nie używamy po prostu ref = {(input) => {input.focus ()}} ? To rozwiązanie działa dla mnie dobrze.
HCLiu
840

Odpowiedź @ Dhiraja jest prawidłowa, a dla wygody możesz użyć rekwizytu autoFocus, aby wejście automatycznie ustawiało ostrość po zamontowaniu:

<input autoFocus name=...

Zauważ, że w jsx jest to autoFocus(wielka litera F) w przeciwieństwie do zwykłego starego kodu HTML, który nie rozróżnia wielkości liter.

Bandyta
źródło
95
Zauważ, że w jsx jego auto F ocus (duże F) w przeciwieństwie do zwykłego starego HTML, który nie rozróżnia wielkości liter.
prauchfuss
8
Bardzo dobrze, dotarłem tutaj po długim bezowocnym wyszukiwaniu :) FYI - Skończyło się na React.DOM.input ({type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target. select ();}})
mlo55
4
Uważam, że autofocus działa tylko przy renderowaniu na pierwszej stronie. Zobacz codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111 wejście powinno się skupić po 3 sekundach.
Eric Andrew Lewis,
45
+1 dla tej metody. Warto wspomnieć, że to nie tylko używa niewiarygodnego autofocusatrybutu HTML5 , ale tak naprawdę używa go focus()w montażu DOM,react-dom więc jest całkiem niezawodny.
Aaron Beall
3
Nie tylko „dla wygody”, ale także jeśli Twój komponent jest funkcjonalny.
phillyslick
167

Od React 0.15 najbardziej zwięzłą metodą jest:

<input ref={input => input && input.focus()}/>
Ilya Semenov
źródło
5
To również obsługuje scenariusze poza początkowym renderowaniem, podczas gdy samo użycie autofokusa nie.
Matt Stannett
pytanie, kiedy dane wejściowe będą fałszywe? Mam na myśli wyrażenie wewnątrz funkcji strzałki.
JaeGeeTee
2
@JaeGeeTee ma wartość null, dopóki komponent nie zostanie zamontowany i / lub po odmontowaniu (nie pamiętam na pewno, co się dzieje).
Ilya Semenov
12
Jedyny problem polega na tym, że koncentruje się on na każdym renderowaniu, które może nie być pożądane.
Jaroslav Benc
Nie działa w moim przypadku (przy użyciu Ant projekt komponentu wejściowego)
vsync
117

Skoncentruj się na montażu

Jeśli chcesz tylko skupić element podczas montowania (początkowo renderuje), wystarczy użyć atrybutu autoFocus.

<input type="text" autoFocus />

Dynamiczny fokus

Aby dynamicznie kontrolować fokus, użyj funkcji ogólnej, aby ukryć szczegóły implementacji przed komponentami.

React 16,8 + Komponent funkcjonalny - użyj haka Focus

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 ] 
}

Pełna wersja demonstracyjna

React 16.3 + Class Components - useFocus

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} 
}

Pełna wersja demonstracyjna

Ben Carp
źródło
2
Ta odpowiedź zawiera właściwe podejście do React Hook. Wspaniały! Nie sprawdza typu, jak jest w TypeScript, ale jest jeden (brzydki) sposób, aby działał: (1) (htmlElRef.current as any).focus()i (2) return {htmlElRef, setFocus}zamiast tablicy.
Ahmed Fasih,
@AhmedFasih, jestem świadomy tego, co mówisz, ale myślę, że jest poza zakresem tego wątku. Jeśli zwrócisz obiekt, trudniej jest kontrolować nazwę zmiennej, co może być problemem, jeśli chcesz użyć useFocuswięcej niż jednego elementu.
Ben Carp
8
Tutaj jest useFocusnapisane pismem maszynowym. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
Ben Carp
2
Super-soczyste, dziękuję !!! Wow, nie wiedziałem o stałych twierdzeniach (które as constmasz), bardzo pouczające!
Ahmed Fasih,
@BenCarp Mała sugestia dotycząca haczyków, może lepiej ustawić setdrugą pozycję jak const [inputRef, setInputFocus] = useFocus(). To pasuje do useState więcej. Najpierw obiekt, a następnie seter tego obiektu
Rubanov,
59

Jeśli chcesz tylko ustawić autofokus w React, jest to proste.

<input autoFocus type="text" />

Jeśli chcesz po prostu wiedzieć, gdzie umieścić ten kod, odpowiedź znajduje się w module componentDidMount ().

v014.3

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

W większości przypadków możesz dołączyć odwołanie do węzła DOM i w ogóle unikać używania findDOMNode.

Przeczytaj dokumenty API tutaj: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode

Jack Lee
źródło
9
I pamiętaj, aby to wykorzystać F! (Uwaga dla siebie i innych, a nie dla odpowiedzi).
2540625
29

W React 16.3 dodano nowy wygodny sposób na poradzenie sobie z tym poprzez utworzenie referencji w konstruktorze komponentu i użycie go w następujący sposób:

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

Aby uzyskać więcej informacji, możesz sprawdzić ten artykuł na blogu React.

Aktualizacja:

Począwszy od React 16.8 , useRefhak może być używany w komponentach funkcji, aby osiągnąć ten sam wynik:

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

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

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

  return (
    <div>
      <input ref={textInput} />
    </div>
  );
};
ettanany
źródło
26

Właśnie natknąłem się na ten problem i używam reagowania 15.0.1 15.0.2 i używam składni ES6 i nie otrzymałem tego, czego potrzebowałem od innych odpowiedzi, odkąd wer. 15 spadło kilka tygodni temu i niektórych this.refswłaściwości były przestarzałe i usunięte .

Ogólnie potrzebowałem:

  1. Skoncentruj pierwszy element wejściowy (pole) podczas montowania komponentu
  2. Ustaw pierwszy element wejściowy (pole) z błędem (po przesłaniu)

Używam:

  • Reaguj pojemnik / element prezentacji
  • Redux
  • React-Router

Skoncentruj pierwszy element wejściowy

Użyłem autoFocus={true}pierwszego <input />na stronie, aby po zamontowaniu komponent uzyskał ostrość.

Skoncentruj pierwszy element wejściowy na błędzie

Trwało to dłużej i było bardziej skomplikowane. Trzymam kod, który nie jest odpowiedni dla rozwiązania dla zwięzłości.

Redux Store / State

Potrzebuję stanu globalnego, aby wiedzieć, czy powinienem ustawić fokus i wyłączyć go, gdy był ustawiony, więc nie ustawiam ponownie fokusu podczas ponownego renderowania komponentów (użyję componentDidUpdate()do sprawdzenia ustawienia fokusa). )

Można to zaprojektować według własnego uznania.

{
    form: {
        resetFocus: false,
    }
}

Komponent kontenerowy

Komponent będzie musiał mieć resetfocusustawioną właściwość i callBack, aby wyczyścić właściwość, jeśli zakończy się ustawianie fokusu na sobie.

Pamiętaj też, że zorganizowałem Action Creators w osobne pliki, głównie ze względu na to, że mój projekt jest dość duży i chciałem podzielić je na łatwiejsze do zarządzania części.

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

Komponent prezentacji

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
}

Przegląd

Ogólna idea jest taka, że ​​każde pole formularza, które może zawierać błąd i być zogniskowane, musi sprawdzić się i czy musi skupić się na sobie.

Istnieje logika biznesowa, która musi się zdarzyć, aby określić, czy dane pole jest właściwym polem, na które należy ustawić fokus. Nie jest to pokazane, ponieważ będzie zależeć od indywidualnej aplikacji.

Po przesłaniu formularza to zdarzenie musi ustawić flagę globalnego fokusa resetFocusna wartość true. Następnie, gdy każdy komponent aktualizuje się sam, zobaczy, że powinien sprawdzić, czy uzyska fokus, a jeśli tak, wyślij zdarzenie, aby zresetować fokus, aby inne elementy nie musiały sprawdzać dalej.

edytuj Na marginesie, miałem logikę biznesową w pliku „narzędzi”, po prostu wyeksportowałem metodę i wywołałem ją w każdymshouldfocus() metody.

Twoje zdrowie!

jmbertucci
źródło
25

Dokumenty React mają teraz sekcję na ten temat. 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();
        }
      }} />
    );
  },
Kevin Suttle
źródło
1
Myślę, że to dobry sposób na zrobienie tego w tym konkretnym scenariuszu.
fabio.sussetto
Nie musiałem autofocusmontować, po prostu szukałem elementu, który pozostanie skupiony podczas wprowadzania wartości. To zadziałało idealnie dla tego scenariusza. (przy użyciu reakcji 15)
Matt Parrilla
13

To nie jest już najlepsza odpowiedź. Począwszy od wersji 0.13, this.refsmoże być niedostępna do czasu componentDidMount()uruchomienia PO , w niektórych dziwnych przypadkach.

Wystarczy dodać autoFocusznacznik do pola wejściowego, jak pokazano powyżej FakeRainBrigand.

GAEfan
źródło
4
Wiele <input autofocus>pól nie będzie się ładnie zachowywać
ᆼ ᆺ ᆼ
4
Oczywiście nie. Tylko jeden fokus na stronę. Jeśli masz wiele autofokusów, powinieneś sprawdzić swój kod i intencje.
GAEfan
2
Pytanie Dave'a dotyczyło skupienia się na <input>renderowaniu po
ᆼ ᆺ ᆼ
1
Czy w przypadku autofokusa można zmusić również klawiaturę iOS do otwarcia?
Remi Sture
1
@RemiSture te same pytania. Czy ktoś ma rozwiązanie tego problemu?
Nam Lê Quý,
12

Nr ref. Komentarz Dave'a do odpowiedzi @ Dhiraja; alternatywą jest użycie funkcji wywołania zwrotnego atrybutu ref na renderowanym elemencie (po pierwszym renderowaniu komponentu):

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

Więcej informacji

o01
źródło
Kiedy to wypróbowałem, otrzymałem:Uncaught TypeError: Cannot read property 'focus' of null
reectrix
1
Musisz zerować parametr, będzie on zerowy, gdy komponent nie zostanie zamontowany. To takie proste component && React.findDomNode.... Przeczytaj więcej na ten temat tutaj: facebook.github.io/react/docs/…
Per Wiklander
12

To jest właściwy sposób na automatyczne ustawianie ostrości. Gdy jako wartość referencyjną użyjesz wywołania zwrotnego zamiast ciągu, jest on automatycznie wywoływany. Masz dostępne ref bez konieczności dotykania DOM przy użyciugetDOMNode

render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},
Pavel Hasala
źródło
2
co z formą kontrolowaną?
piksel 67
@ pixel67 Również. Możesz ustawić odniesienie do elementów, ale także komponentów. Ale musisz być tego świadomy podczas pracy z nim. Więc nie będziesz próbował uzyskać dostępu do wartości wejściowej, jeśli ustawisz referencję na React.Component, która otacza wejście HTML.
Pavel Hasala,
10

Zauważ, że żadna z tych odpowiedzi nie działała dla mnie z komponentem TextField w interfejsie użytkownika . Na Jak ustawić fokus na materialowym interfejsie tekstowym? Musiałem przeskoczyć przez kilka obręczy, aby to zadziałało:

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

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);
Lane Rettig
źródło
2
Wygląda na to, że jeśli komponent się animuje, wywołanie do focus()musi być opóźnione do końca animacji.
adriendenat
9

Możesz umieścić to wywołanie metody w funkcji renderowania. Lub w metodzie cyklu życia,componentDidUpdate

Raza Gill
źródło
1
componentDidUpdate działa w moim przypadku. Musiałem ustawić fokus na konkretnym przycisku po wywołaniu renderowania.
FariaC,
8

Nie potrzebujesz getInputDOMNode?? w tym przypadku...

Po prostu pobierz refi focus()to, gdy komponent zostanie zamontowany - 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'));
Alireza
źródło
5

AutoFocus działał dla mnie najlepiej. Musiałem zamienić tekst na wejściowy z tym tekstem po podwójnym kliknięciu, więc oto co skończyło się na:

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

UWAGA: Aby rozwiązać problem polegający na tym, że React umieszcza kursor na początku tekstu, użyj tej metody:

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

Znaleziono tutaj: https://coderwall.com/p/0iz_zq/how-to-put-focus-at-the-end-of-an-input-with-react-js

Luke Watts
źródło
3

Mam ten sam problem, ale mam też animację, więc mój kolega sugeruje użycie window.requestAnimationFrame

to jest atrybut ref mojego elementu:

ref={(input) => {input && window.requestAnimationFrame(()=>{input.focus()})}}
SHI1485
źródło
2

Ostrzeżenie: ReactDOMComponent: Nie uzyskuj dostępu do .getDOMNode () węzła DOM; zamiast tego użyj bezpośrednio węzła. Ten węzeł DOM został zrenderowany przez App.

Powinien być

componentDidMount: function () {
  this.refs.nameInput.focus();
}
Kirk Strobeck
źródło
2

Najprostszą odpowiedzią jest dodanie ref = "some name" w wejściowym elemencie tekstowym i wywołanie poniższej funkcji.

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

<input type="text" ref="field_name" />
Venkatesh Somu
źródło
2

Aby przenieść fokus na nowo utworzony element, możesz zapisać identyfikator elementu w stanie i użyć go do ustawienia autoFocus. na przykład

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

W ten sposób żadne z pól tekstowych nie zostanie zogniskowane na ładowaniu strony (tak jak chcę), ale kiedy naciśniesz przycisk „Dodaj”, aby utworzyć nowy rekord, wówczas ten nowy rekord zostanie zogniskowany.

Ponieważ autoFocusnie „uruchamia się” ponownie, chyba że komponent zostanie ponownie zamontowany, nie muszę się przejmować rozbrajaniem this.state.focus(tzn. Nie będzie nadal kradł fokusu, gdy aktualizuję inne stany).

mpen
źródło
1

Przeczytałem prawie całą odpowiedź, ale nie widziałem getRenderedComponent().props.input

Ustaw referencje wprowadzania tekstu

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

nisza
źródło
Proszę wyjaśnić swoją odpowiedź w kontekście ich kodu.
Jimmy Smith
1

Po wypróbowaniu wielu powyższych opcji bez powodzenia stwierdziłem, że było tak, jak byłem, disablinga następnie enablingdane wejściowe, które spowodowały utratę koncentracji.

Miałem rekwizyt, sendingAnswerktóry wyłączyłby Wejście podczas odpytywania backendu.

<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.
/>

Gdy usunąłem wyłączony rekwizyt, wszystko znów zaczęło działać.

Jacek
źródło
0

Zaktualizowana wersja, którą możesz sprawdzić tutaj

componentDidMount() {

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

}
render() {
    return <input type="text" ref={(input) => { this.inputRef = input }} />
})
Nijat Ahmadov
źródło
0

Ponieważ istnieje wiele przyczyn tego błędu, pomyślałem, że opublikuję również problem, z którym się spotkałem. Dla mnie problemem było to, że renderowałem moje dane wejściowe jako treść innego komponentu.

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

Tak nazwałem powyższy komponent:

<Component Content={() => {
  return (
    <input type="text"/>
  );
}} />
Dean Koštomaj
źródło
0

Zgodnie ze zaktualizowaną składnią można użyć this.myRref.current.focus()

Arun Kumar
źródło