Cara menggunakan referensi di React with Typescript

139

Saya menggunakan Typecript dengan React. Saya mengalami kesulitan memahami bagaimana menggunakan ref untuk mendapatkan pengetikan statis dan intellisense sehubungan dengan node react yang direferensikan oleh ref. Kode saya adalah sebagai berikut.

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
sumber

Jawaban:

183

Jika Anda menggunakan React 16.3+, cara yang disarankan untuk membuat referensi adalah menggunakan 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} />;
    }
}

Saat komponen dipasang, properti refatribut currentakan ditetapkan ke komponen yang direferensikan / elemen DOM dan ditugaskan kembali ke nullsaat dilepas. Jadi, misalnya, Anda bisa mengaksesnya menggunakan this.stepInput.current.

Untuk lebih lanjut RefObject, lihat jawaban @ apieceofbart atau PR createRef() ditambahkan.


Jika Anda menggunakan React versi sebelumnya (<16.3) atau membutuhkan lebih banyak kendali atas kapan ref disetel dan tidak disetel, Anda bisa menggunakan "callback refs" .

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

Saat komponen dipasang, React akan memanggil refcallback dengan elemen DOM, dan akan memanggilnya dengan nullsaat dilepas. Jadi, misalnya, Anda dapat mengaksesnya hanya dengan menggunakan this.stepInput.

Dengan mendefinisikan refcallback sebagai metode terikat pada kelas sebagai kebalikan dari fungsi inline (seperti dalam versi sebelumnya dari jawaban ini), Anda dapat menghindari callback dipanggil dua kali selama update.


Ada yang digunakan untuk menjadi sebuah API mana refatribut adalah string (lihat jawaban Akshar Patel ), namun karena beberapa masalah , ref string sangat tidak dianjurkan dan akhirnya akan dihapus.


Diedit 22 Mei 2018 untuk menambahkan cara baru dalam melakukan ref di React 16.3. Terima kasih @apieceofbart karena telah menunjukkan bahwa ada cara baru.

Jeff Bowen
sumber
Perhatikan cara ini lebih disukai. Contoh di bawah dengan refsatribut class tidak akan digunakan lagi dalam versi React yang akan datang.
Jimi Pajala
1
Harap dicatat bahwa ini sudah cara lama :) saat ini adalah menggunakan React.createRef ()
apieceofbart
@apieceofbart Terima kasih atas perhatiannya. Memperbarui jawaban untuk memasukkan cara baru.
Jeff Bowen
2
Saya hanya tidak melihat apa-apa tentang ketikan dalam jawaban Anda, saya akan menambahkan jawaban lain
masing
Ups. Memiliki Ketikan dalam jawaban asli saya tetapi lupa memasukkannya ke yang baru. Menambahkannya kembali dan ditautkan ke jawaban Anda juga. Terima kasih.
Jeff Bowen
30

Salah satu cara (yang telah saya lakukan ) adalah mengatur secara manual:

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

maka Anda bahkan dapat membungkus ini dalam fungsi pengambil yang lebih baik (misalnya di sini ):

stepInput = (): HTMLInputElement => ReactDOM.findDOMNode(this.refs.stepInput);
basarat
sumber
1
Terima kasih @basarat. Saya mencoba solusi Anda tetapi saya mendapatkan kesalahan ini 'Type Element is not assignable to type' HTMLInputElement. Penerimaan properti hilang dalam elemen tipe ''
Akshar Patel
Mungkin ada masalah dengan versi terbaru dari definisi react-dom. Gunakan sebagai pernyataan sementara itu
basarat
Jelas anytidak wajib di sini. Sebagian besar contoh yang saya lihat digunakan HTMLInputElement. Hanya menyatakan yang sudah jelas, tetapi jika PeoplePickerreferensi Anda menggunakan komponen React (yaitu ), Anda dapat menggunakan komponen itu sebagai tipe untuk mendapatkan pengetikan.
Joe Martella
24

Sejak React 16.3 cara untuk menambahkan ref adalah dengan menggunakan React.createRef seperti yang ditunjukkan Jeff Bowen dalam jawabannya. Namun Anda bisa memanfaatkan Ketikan untuk mengetik referensi Anda dengan lebih baik.

Dalam contoh Anda, Anda menggunakan ref pada elemen masukan. Jadi cara saya melakukannya adalah:

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

    ...

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

Dengan melakukan ini ketika Anda ingin menggunakan referensi itu, Anda memiliki akses ke semua metode masukan:

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

Anda juga dapat menggunakannya pada komponen khusus:

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

dan kemudian memiliki, misalnya, akses ke properti:

this.componentRef.current.props; // 'props' satisfy IProps interface
masing-masing
sumber
17

EDIT: Ini bukan lagi cara yang tepat untuk menggunakan referensi dengan Ketikan. Lihat jawaban Jeff Bowen dan beri suara positif untuk meningkatkan visibilitasnya.

Menemukan jawaban atas masalah tersebut. Gunakan referensi seperti di bawah ini di dalam kelas.

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

Terima kasih @basarat karena telah menunjuk ke arah yang benar.

Akshar Patel
sumber
2
Saya masih mendapatkan Property 'stepInput' does not exist on type '{ [key: string]: Component<any, any> | Element; }', saat mencoba mengakses this.refs.stepInput.
Nik Sumeiko
@NikSumeiko, Anda mendapatkan kesalahan itu karena refsobjek Anda hanya memiliki [key: string]entri.
Joe Martella
9

React.createRef (komponen kelas)

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

Catatan: Menghilangkan API lama String Refs di sini ...


React.useRef (Pengait / komponen fungsi)

Referensi hanya baca untuk simpul DOM:
const FunctionApp = () => {
  const inputRef = React.useRef<HTMLInputElement>(null) // note the passed in `null` arg
  return <input type="text" ref={inputRef} />
}
Referensi yang dapat diubah untuk nilai yang disimpan sewenang-wenang:
const FunctionApp = () => {
  const renderCountRef = useRef(0)
  useEffect(() => {
    renderCountRef.current += 1
  })
  // ... other render code
}

Catatan: Jangan menginisialisasi useRefdengan nulldalam kasus ini. Itu akan membuat renderCountReftipe readonly(lihat contoh ). Jika Anda perlu memberikan nullsebagai nilai awal, lakukan ini:

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

Referensi panggilan balik (berfungsi untuk keduanya)

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

Sampel taman bermain

ford04
sumber
Apa perbedaan antara useRef() as MutableRefObject<HTMLInputElement>dan useRef<HTMLInputElement>(null)?
ksav
2
Pertanyaan bagus - currentproperti dari MutableRefObject<HTMLInputElement>dapat dimodifikasi, sedangkan useRef<HTMLInputElement>(null)membuat RefObjecttipe dengan currentditandai sebagai readonly. Yang pertama dapat digunakan, jika Anda perlu mengubah node DOM saat ini di ref sendiri, misalnya dalam kombinasi dengan pustaka eksternal. Hal ini juga dapat ditulis tanpa as: useRef<HTMLInputElement | null>(null). Yang terakhir adalah pilihan yang lebih baik untuk node DOM yang dikelola React, seperti yang digunakan dalam banyak kasus. React menyimpan node di ref itu sendiri dan Anda tidak ingin mencoba-coba mengubah nilai-nilai ini.
ford04
1
Terimakasih atas klarifikasinya.
ksav
7

Jika Anda menggunakan React.FC, tambahkan HTMLDivElementantarmuka:

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

Dan gunakan seperti berikut:

return <div ref={myRef} />;
JulienRioux
sumber
1
Terima kasih. Tip lain bagi siapa pun yang menemukan ini adalah memeriksa Elemen. Contoh ini mengacu pada penggunaan elemen DIV. Sebuah formulir misalnya akan menggunakan - const formRef = React.useRef <HTMLFormElement> (null);
Nick Taras
1
Terima kasih terima kasih terima kasih terima kasih terima kasih terima kasih terima kasih terima kasih. Terima kasih.
Ambrown
2

Untuk menggunakan gaya panggilan balik ( https://facebook.github.io/react/docs/refs-and-the-dom.html ) seperti yang direkomendasikan pada dokumentasi React, Anda dapat menambahkan definisi untuk properti di kelas:

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
sumber
1

Kurangnya contoh lengkap, berikut adalah skrip tes kecil saya untuk mendapatkan input pengguna saat bekerja dengan React dan TypeScript. Berdasarkan sebagian komentar lain dan tautan ini 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
sumber
1

Untuk pengguna skrip tidak diperlukan konstruktor.

...

private divRef: HTMLDivElement | null = null

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

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

...

Morlo Mbakop
sumber
0

Dari definisi tipe React

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

Jadi Anda dapat mengakses elemen referensi Anda sebagai berikut

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

tanpa redefinisi indeks referensi.

Seperti yang disebutkan @manakor, Anda bisa mendapatkan error seperti

Properti 'stepInput' tidak ada pada tipe '{[key: string]: Component | Elemen; }

jika Anda mendefinisikan ulang ref (tergantung pada IDE dan versi ts yang Anda gunakan)

Dzmitry Valadziankou
sumber
0

Hanya untuk menambahkan pendekatan yang berbeda - Anda cukup memberikan referensi Anda, seperti:

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

Saya selalu melakukan ini, dalam hal itu untuk meraih wasit

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

pengguna2662112
sumber
biarkan masukan: HTMLInputElement = ReactDOM.findDOMNode <HTMLInputElement> (this.refs ['input']);
pengguna2662112
0

Jika Anda tidak ingin meneruskan ref, dalam antarmuka Props Anda perlu menggunakan RefObject<CmpType>tipeimport React, { RefObject } from 'react';

MiF
sumber
0

Bagi mereka yang mencari cara melakukannya ketika Anda memiliki serangkaian elemen:

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

letakkan di suatu objek

varman
sumber
1
Tolong jelaskan jawaban Anda
AesSedai101
Jawaban ini menyetel ctrls.input ke elemen yang diketik dengan kuat, yang merupakan cara yang sangat diketik. Ini adalah pilihan "Ketikan" yang lebih baik.
Doug