Verwendung von Refs in Reagieren mit Typoskript

139

Ich verwende Typescript mit React. Ich habe Probleme zu verstehen, wie Refs verwendet werden, um statische Typisierung und Intellisense in Bezug auf die von den Refs referenzierten Reaktionsknoten zu erhalten. Mein Code lautet wie folgt.

import * as React from 'react';

interface AppState {
    count: number;
}

interface AppProps {
    steps: number;
}

interface AppRefs {
    stepInput: HTMLInputElement;
}

export default class TestApp extends React.Component<AppProps, AppState> {

constructor(props: AppProps) {
    super(props);
    this.state = {
        count: 0
    };
}

incrementCounter() {
    this.setState({count: this.state.count + 1});
}

render() {
    return (
        <div>
            <h1>Hello World</h1>
            <input type="text" ref="stepInput" />
            <button onClick={() => this.incrementCounter()}>Increment</button>
            Count : {this.state.count}
        </div>
    );
}}
Akshar Patel
quelle

Antworten:

183

Wenn Sie React 16.3+ verwenden, wird die vorgeschlagene Methode zum Erstellen von Refs verwendet React.createRef().

class TestApp extends React.Component<AppProps, AppState> {
    private stepInput: React.RefObject<HTMLInputElement>;
    constructor(props) {
        super(props);
        this.stepInput = React.createRef();
    }
    render() {
        return <input type="text" ref={this.stepInput} />;
    }
}

Wenn die Komponente bereitgestellt wird, refwird die currentEigenschaft des Attributs dem referenzierten Komponenten- / DOM-Element zugewiesen und nullbeim Aufheben der Bereitstellung zurück zugewiesen. So können Sie beispielsweise mit darauf zugreifen this.stepInput.current.

Weitere Informationen findenRefObject Sie unter @ apieceofbarts Antwort oder die PR createRef() wurde hinzugefügt.


Wenn Sie eine frühere Version von React (<16.3) verwenden oder eine genauere Kontrolle darüber benötigen, wann Refs gesetzt und deaktiviert werden, können Sie "Callback Refs" verwenden .

class TestApp extends React.Component<AppProps, AppState> {
    private stepInput: HTMLInputElement;
    constructor(props) {
        super(props);
        this.stepInput = null;
        this.setStepInputRef = element => {
            this.stepInput = element;
        };
    }
    render() {
        return <input type="text" ref={this.setStepInputRef} />
    }
}

Wenn die Komponente bereitgestellt wird, ruft React den refRückruf mit dem DOM-Element auf und ruft ihn auf, nullwenn die Bereitstellung aufgehoben wird. So können Sie beispielsweise einfach mit darauf zugreifen this.stepInput.

Indem Sie den refRückruf als gebundene Methode für die Klasse im Gegensatz zu einer Inline-Funktion definieren (wie in einer früheren Version dieser Antwort), können Sie verhindern, dass der Rückruf bei Aktualisierungen zweimal aufgerufen wird .


Es verwendet zu sein , eine API , wo das refAttribut war ein String (siehe Akshar Patel Antwort ), aber aufgrund einiger Probleme , string Refs dringend abgeraten werden und wird schließlich entfernt werden.


Bearbeitet am 22. Mai 2018, um die neue Methode zum Ausführen von Refs in React 16.3 hinzuzufügen. Vielen Dank an @apieceofbart für den Hinweis, dass es einen neuen Weg gibt.

Jeff Bowen
quelle
Beachten Sie, dass dies der bevorzugte Weg ist. Die folgenden Beispiele mit refsKlassenattribut werden in kommenden React-Versionen nicht mehr unterstützt.
Jimi Pajala
1
Bitte beachten Sie, dass dies bereits ein alter Weg ist :) Derzeit ist die Verwendung von React.createRef ()
apieceofbart
@apieceofbart Danke für das Heads Up. Die Antwort wurde aktualisiert, um den neuen Weg einzuschließen.
Jeff Bowen
2
Ich sehe nur nichts über Typoskript in Ihrer Antwort, ich werde eine weitere Antwort hinzufügen
pro Stück Bart
Hoppla. Hatte Typoskript in meiner ursprünglichen Antwort, vergaß aber, es in die neue aufzunehmen. Fügte es wieder hinzu und verknüpfte es auch mit Ihrer Antwort. Vielen Dank.
Jeff Bowen
30

Eine Möglichkeit (die ich gemacht habe ) ist die manuelle Einrichtung:

refs: {
    [string: string]: any;
    stepInput:any;
}

dann können Sie dies sogar in eine schönere Getter-Funktion einwickeln (zB hier ):

stepInput = (): HTMLInputElement => ReactDOM.findDOMNode(this.refs.stepInput);
Basarat
quelle
1
Danke @basarat. Ich habe Ihre Lösung ausprobiert, erhalte jedoch die Fehlermeldung 'Type Element kann nicht dem Typ' HTMLInputElement zugewiesen werden. Die Annahme der Eigenschaft fehlt im Typ Element ''
Akshar Patel
Möglicherweise liegt ein Problem mit der neueren Version der React-Dom-Definitionen vor. Verwenden Sie als Behauptung in der Zwischenzeit
Basarat
Offensichtlich anyist hier nicht obligatorisch. Die meisten Beispiele sehe ich Verwendung HTMLInputElement. Nur das Offensichtliche angeben, aber wenn sich Ihr Verweis auf einer React-Komponente befindet (dh PeoplePicker), können Sie diese Komponente als Typ verwenden, um Typisierungen abzurufen.
Joe Martella
23

Seit React 16.3 können Sie Refs hinzufügen, indem Sie React.createRef verwenden, wie Jeff Bowen in seiner Antwort gezeigt hat. Sie können jedoch Typescript nutzen, um Ihre Referenz besser einzugeben.

In Ihrem Beispiel verwenden Sie ref on input element. So würde ich es machen:

class SomeComponent extends React.Component<IProps, IState> {
    private inputRef: React.RefObject<HTMLInputElement>;
    constructor() {
        ...
        this.inputRef = React.createRef();
    }

    ...

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

Auf diese Weise haben Sie Zugriff auf alle Eingabemethoden, wenn Sie diese Referenz verwenden möchten:

someMethod() {
    this.inputRef.current.focus(); // 'current' is input node, autocompletion, yay!
}

Sie können es auch für benutzerdefinierte Komponenten verwenden:

private componentRef: React.RefObject<React.Component<IProps>>;

und dann zum Beispiel Zugriff auf Requisiten haben:

this.componentRef.current.props; // 'props' satisfy IProps interface
pro Stück Bart
quelle
17

BEARBEITEN: Dies ist nicht mehr der richtige Weg, um Refs mit Typescript zu verwenden. Schauen Sie sich Jeff Bowens Antwort an und stimmen Sie sie ab, um ihre Sichtbarkeit zu erhöhen.

Ich habe die Antwort auf das Problem gefunden. Verwenden Sie die folgenden Referenzen in der Klasse.

refs: {
    [key: string]: (Element);
    stepInput: (HTMLInputElement);
}

Vielen Dank an @basarat für den Hinweis in die richtige Richtung.

Akshar Patel
quelle
2
Ich bekomme immer noch Property 'stepInput' does not exist on type '{ [key: string]: Component<any, any> | Element; }', wenn ich versuche zuzugreifen this.refs.stepInput.
Nik Sumeiko
@NikSumeiko, Sie haben diesen Fehler erhalten, weil Ihr refsObjekt nur den [key: string]Eintrag hatte.
Joe Martella
8

React.createRef (Klassenkomponenten)

class ClassApp extends React.Component {
  inputRef = React.createRef<HTMLInputElement>();
  
  render() {
    return <input type="text" ref={this.inputRef} />
  }
}

Hinweis: Hier wird die alte String Refs- Legacy-API weggelassen ...


React.useRef (Haken / Funktionskomponenten)

Schreibgeschützte Referenzen für DOM-Knoten:
const FunctionApp = () => {
  const inputRef = React.useRef<HTMLInputElement>(null) // note the passed in `null` arg
  return <input type="text" ref={inputRef} />
}
Veränderbare Referenzen für beliebig gespeicherte Werte:
const FunctionApp = () => {
  const renderCountRef = useRef(0)
  useEffect(() => {
    renderCountRef.current += 1
  })
  // ... other render code
}

Hinweis: In diesem Fall nicht initialisieren useRefmit null. Es würde den renderCountRefTyp machen readonly(siehe Beispiel ). Wenn Sie benötigen bieten nullals Anfangswert, dies zu tun:

const renderCountRef = useRef<number | null>(null)

Rückrufreferenzen (funktionieren für beide)

// Function component example 
const FunctionApp = () => {
  const handleDomNodeChange = (domNode: HTMLInputElement | null) => {
    // ... do something with changed dom node.
  }
  return <input type="text" ref={handleDomNodeChange} />
}

Spielplatz Probe

ford04
quelle
Was ist der Unterschied zwischen useRef() as MutableRefObject<HTMLInputElement>und useRef<HTMLInputElement>(null)?
ksav
2
Gute Frage - die currentEigenschaft von MutableRefObject<HTMLInputElement>kann geändert werden, während useRef<HTMLInputElement>(null)ein RefObjectTyp mit der currentMarkierung als erstellt wird readonly. Ersteres kann verwendet werden, wenn Sie aktuelle DOM-Knoten in refs selbst ändern müssen, z. B. in Kombination mit einer externen Bibliothek. Es kann auch geschrieben werden ohne as: useRef<HTMLInputElement | null>(null). Letzteres ist eine bessere Wahl für React-verwaltete DOM-Knoten, wie sie in den meisten Fällen verwendet werden. React speichert die Knoten in den Refs selbst und Sie möchten nicht herumfummeln, um diese Werte zu ändern.
ford04
1
Danke für die Klarstellung.
ksav
7

Wenn Sie verwenden React.FC, fügen Sie die HTMLDivElementSchnittstelle hinzu:

const myRef = React.useRef<HTMLDivElement>(null);

Und verwenden Sie es wie folgt:

return <div ref={myRef} />;
JulienRioux
quelle
1
Vielen Dank. Ein weiterer Tipp für alle, die darauf stoßen, ist das Überprüfen des Elements. Dieses Beispiel bezieht sich auf die Verwendung eines DIV-Elements. Ein Formular würde beispielsweise Folgendes verwenden: const formRef = React.useRef <HTMLFormElement> (null);
Nick Taras
1
Danke, danke, danke, danke, danke, danke, danke, danke, danke. Danke dir.
Ambrown
2

Um den in der React-Dokumentation empfohlenen Rückrufstil ( https://facebook.github.io/react/docs/refs-and-the-dom.html ) zu verwenden, können Sie eine Definition für eine Eigenschaft in der Klasse hinzufügen:

export class Foo extends React.Component<{}, {}> {
// You don't need to use 'references' as the name
references: {
    // If you are using other components be more specific than HTMLInputElement
    myRef: HTMLInputElement;
} = {
    myRef: null
}
...
 myFunction() {
    // Use like this
    this.references.myRef.focus();
}
...
render() {
    return(<input ref={(i: any) => { this.references.myRef = i; }}/>)
}
lucavgobbi
quelle
1

Da mir kein vollständiges Beispiel fehlt, ist hier mein kleines Testskript zum Abrufen von Benutzereingaben bei der Arbeit mit React und TypeScript. Teilweise basierend auf den anderen Kommentaren und diesem Link https://medium.com/@basarat/strongly-typed-refs-for-react-typescript-9a07419f807#.cdrghertm

/// <reference path="typings/react/react-global.d.ts" />

// Init our code using jquery on document ready
$(function () {
    ReactDOM.render(<ServerTime />, document.getElementById("reactTest"));
});

interface IServerTimeProps {
}

interface IServerTimeState {
    time: string;
}

interface IServerTimeInputs {
    userFormat?: HTMLInputElement;
}

class ServerTime extends React.Component<IServerTimeProps, IServerTimeState> {
    inputs: IServerTimeInputs = {};

    constructor() {
        super();
        this.state = { time: "unknown" }
    }

    render() {
        return (
            <div>
                <div>Server time: { this.state.time }</div>
                <input type="text" ref={ a => this.inputs.userFormat = a } defaultValue="s" ></input>
                <button onClick={ this._buttonClick.bind(this) }>GetTime</button>
            </div>
        );
    }

    // Update state with value from server
    _buttonClick(): void {
    alert(`Format:${this.inputs.userFormat.value}`);

        // This part requires a listening web server to work, but alert shows the user input
    jQuery.ajax({
        method: "POST",
        data: { format: this.inputs.userFormat.value },
        url: "/Home/ServerTime",
        success: (result) => {
            this.setState({ time : result });
        }
    });
}

}}

Tikall
quelle
1

Für Typoskriptbenutzer ist kein Konstruktor erforderlich.

...

private divRef: HTMLDivElement | null = null

getDivRef = (ref: HTMLDivElement | null): void => {
    this.divRef = ref
}

render() {
    return <div ref={this.getDivRef} />
}

...

Morlo Mbakop
quelle
0

Aus der Definition des Reaktionstyps

    type ReactInstance = Component<any, any> | Element;
....
    refs: {
            [key: string]: ReactInstance
    };

So können Sie wie folgt auf Ihr refs-Element zugreifen

stepInput = () => ReactDOM.findDOMNode(this.refs['stepInput']);

ohne Neudefinition des Referenzindex.

Wie @manakor erwähnt, können Sie Fehler wie bekommen

Die Eigenschaft 'stepInput' ist für den Typ '{[key: string]: Component | nicht vorhanden Element; }}

Wenn Sie Refs neu definieren (abhängig von der verwendeten IDE- und TS-Version)

Dzmitry Valadziankou
quelle
0

Nur um einen anderen Ansatz hinzuzufügen - Sie können einfach Ihren Schiedsrichter besetzen, so etwas wie:

let myInputElement: Element = this.refs["myInput"] as Element
Dimitar Dimitrov
quelle
0

Ich mache das immer, in diesem Fall, um mir einen Schiedsrichter zu schnappen

let input: HTMLInputElement = ReactDOM.findDOMNode<HTMLInputElement>(this.refs.input);

user2662112
quelle
let input: HTMLInputElement = ReactDOM.findDOMNode <HTMLInputElement> (this.refs ['input']);
user2662112
0

Wenn Sie Ihre nicht weiterleiten möchten, müssen Sie refin der Props-Oberfläche den RefObject<CmpType>Typ from verwendenimport React, { RefObject } from 'react';

MiF
quelle
0

Für diejenigen, die sich ansehen, wie es geht, wenn Sie eine Reihe von Elementen haben:

const textInputRefs = useRef<(HTMLDivElement | null)[]>([])

...

const onClickFocus = (event: React.BaseSyntheticEvent, index: number) => {
    textInputRefs.current[index]?.focus()
};

...

{items.map((item, index) => (
    <textInput
        inputRef={(ref) => textInputs.current[index] = ref}
    />
    <Button
        onClick={event => onClickFocus(event, index)}
    />
}
Jöcker
quelle
-1
class SelfFocusingInput extends React.Component<{ value: string, onChange: (value: string) => any }, {}>{
    ctrls: {
        input?: HTMLInputElement;
    } = {};
    render() {
        return (
            <input
                ref={(input) => this.ctrls.input = input}
                value={this.props.value}
                onChange={(e) => { this.props.onChange(this.ctrls.input.value) } }
                />
        );
    }
    componentDidMount() {
        this.ctrls.input.focus();
    }
}

lege sie in ein Objekt

Varman
quelle
1
Bitte erklären Sie Ihre Antwort
AesSedai101
Diese Antwort setzt ctrls.input auf ein stark typisiertes Element. Dies ist der stark typisierte Weg. Dies ist eine bessere "Typescript" -Wahl.
Doug