React with Typescript'te ref nasıl kullanılır


140

React ile Typescript kullanıyorum. Referanslar tarafından atıfta bulunulan reaksiyon düğümlerine göre statik yazım ve zeka elde etmek için ref'leri nasıl kullanacağımı anlamakta güçlük çekiyorum. Kodum aşağıdaki gibidir.

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

Yanıtlar:


184

React 16.3+ kullanıyorsanız, referans oluşturmanın önerilen yolu kullanmaktır 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} />;
    }
}

Bileşen refbağlandığında , özniteliğin currentözelliği başvurulan bileşene / DOM öğesine atanacak ve nullayrıldığında geri atanacaktır . Yani, örneğin, kullanarak erişebilirsiniz this.stepInput.current.

Daha fazla bilgi RefObjectiçin @ apieceofbart'ın cevabına veya PR createRef() eklendiğine bakın.


React'in önceki bir sürümünü (<16.3) kullanıyorsanız veya referanslar ayarlandığında ve ayarlanmadığında daha ayrıntılı kontrole ihtiyacınız varsa, "geri arama referanslarını" kullanabilirsiniz .

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

Bileşen bağlandığında, React refDOM öğesi ile geri aramayı çağıracak ve nullayrıldığında onu çağıracaktır . Yani, örneğin, onu kullanarak erişebilirsiniz this.stepInput.

refGeri aramayı, bir satır içi işlevin aksine sınıfta ilişkili bir yöntem olarak tanımlayarak (bu cevabın önceki bir sürümünde olduğu gibi), geri aramanın güncellemeler sırasında iki kez çağrılmasını önleyebilirsiniz .


Orada eskiden bir API ref(bkz Öznitelik dize oldu Akshar Patel'in cevabı ), ancak nedeniyle bazı sorunlar , dize hakemler kesinlikle tavsiye edilmez ve sonunda silinecektir.


React 16.3'e yeni başvuru yapma yöntemi eklemek için 22 Mayıs 2018 tarihinde düzenlendi. @ Apieceofbart'a yeni bir yolun olduğunu belirttiğiniz için teşekkürler.


Bunun tercih edilen yol olduğuna dikkat edin. refsSınıf özniteliğine sahip aşağıdaki örnekler , gelecek React sürümlerinde kullanımdan kaldırılacaktır.
Jimi Pajala

1
Lütfen bunun zaten eski bir yol olduğunu unutmayın :) akım React.createRef () kullanmaktır
apieceofbart

@apieceofbart Uyarılar için teşekkürler. Cevap, yeni yolu içerecek şekilde güncellendi.
Jeff Bowen

2
Cevabınızda
typcript

Whoops. Orijinal cevabımda Typescript vardı, ancak yenisine eklemeyi unuttum. Onu geri ekledi ve cevabına da bağladı. Teşekkürler.
Jeff Bowen

30

Bir yol ( benim yaptığım ) manuel olarak ayarlamaktır:

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

o zaman bunu daha güzel bir alıcı işleviyle de tamamlayabilirsiniz (örneğin burada ):

stepInput = (): HTMLInputElement => ReactDOM.findDOMNode(this.refs.stepInput);

1
Teşekkürler @ basarat. Çözümünüzü denedim, ancak "Öğe Türü" HTMLInputElement türüne atanabilir değil "hatasını alıyorum. Öğe türünde mülk kabulü eksik ''
Akshar Patel

React-dom tanımlarının yeni sürümüyle ilgili bir sorun olabilir. Bu arada iddia olarak kullanın
basarat

Açıkçası anyburada zorunlu değil. Kullandığım çoğu örnek HTMLInputElement. Sadece bariz olanı belirtmekle birlikte, eğer PeoplePickerref'iniz bir React bileşeni (yani ) üzerindeyse, yazı elde etmek için bu bileşeni tür olarak kullanabilirsiniz.
Joe Martella

24

React 16.3'ten beri refs eklemenin yolu , Jeff Bowen'in cevabında işaret ettiği gibi React.createRef'i kullanmaktır . Bununla birlikte, referansınızı daha iyi yazmak için Typescript'ten yararlanabilirsiniz.

Örneğinizde, giriş öğesinde ref kullanıyorsunuz. Yani benim yapmamın bir yolu:

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

    ...

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

Bu referansı kullanmak istediğinizde bunu yaparak tüm giriş yöntemlerine erişebilirsiniz:

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

Özel bileşenlerde de kullanabilirsiniz:

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

ve sonra, örneğin, sahne öğelerine erişebilirsiniz:

this.componentRef.current.props; // 'props' satisfy IProps interface

17

DÜZENLEME: Bu artık, Typescript ile referansları kullanmanın doğru yolu değil. Jeff Bowen'in cevabına bakın ve görünürlüğünü artırmak için ona olumlu oy verin.

Sorunun cevabını buldum. Referansları sınıf içinde aşağıdaki gibi kullanın.

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

Doğru yönü gösterdiği için @ basarat'a teşekkürler.


2
Property 'stepInput' does not exist on type '{ [key: string]: Component<any, any> | Element; }'Erişmeye çalışırken hala alıyorum this.refs.stepInput.
Nik Sumeiko

@NikSumeiko, bu hatayı alıyordunuz çünkü refsnesnenizde yalnızca [key: string]giriş vardı .
Joe Martella

9

React.createRef (sınıf bileşenleri)

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

Not: Eski String Refs legacy API'yi burada atlamak ...


React.useRef (Kancalar / fonksiyon bileşenleri)

DOM düğümleri için salt okunur referanslar:
const FunctionApp = () => {
  const inputRef = React.useRef<HTMLInputElement>(null) // note the passed in `null` arg
  return <input type="text" ref={inputRef} />
}
Rasgele depolanan değerler için değiştirilebilir referanslar :
const FunctionApp = () => {
  const renderCountRef = useRef(0)
  useEffect(() => {
    renderCountRef.current += 1
  })
  // ... other render code
}

Not: Bu durumda useRefile başlatmayın null. Bu kılacak renderCountReftipini readonly(bkz örnek ). Eğer varsa gereken sağlamak nullbaşlangıç değeri olarak, şunu yapın:

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

Geri arama referansları (her ikisi için de çalışır)

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

Oyun alanı örneği


Arasındaki fark nedir useRef() as MutableRefObject<HTMLInputElement>ve useRef<HTMLInputElement>(null)?
ksav

2
İyi soru - currentözelliğiMutableRefObject<HTMLInputElement> değiştirilebilir, ancak olarak işaretlenmiş useRef<HTMLInputElement>(null)bir RefObjecttür oluşturur . Referanslardaki mevcut DOM düğümlerini kendiniz değiştirmeniz gerekirse, örneğin harici bir kitaplıkla kombinasyon halinde, birincisi kullanılabilir. Ayrıca olmadan yazılabilir : . İkincisi, çoğu durumda kullanıldığı gibi, React tarafından yönetilen DOM düğümleri için daha iyi bir seçimdir. React, düğümleri referansların kendisinde saklar ve bu değerleri değiştirmekle uğraşmak istemezsiniz. currentreadonlyasuseRef<HTMLInputElement | null>(null)
ford04

1
Açıklama için teşekkürler.
ksav

8

Kullanıyorsanız arayüzü React.FCekleyin HTMLDivElement:

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

Ve aşağıdaki gibi kullanın:

return <div ref={myRef} />;

1
Teşekkürler. Bununla karşılaşan herkes için bir başka ipucu da Element'i kontrol etmektir. Bu örnek, bir DIV öğesinin kullanımına ilişkindir. Örneğin bir form - const formRef = React.useRef <HTMLFormElement> (null);
Nick Taras

1
Teşekkür ederim teşekkür ederim teşekkür ederim teşekkür ederim teşekkür ederim teşekkür ederim teşekkür ederim Teşekkür ederim.
Ambrown

2

Geri çağrı stilini ( https://facebook.github.io/react/docs/refs-and-the-dom.html ) React'in belgelerinde önerildiği gibi kullanmak için , sınıftaki bir özellik için bir tanım ekleyebilirsiniz:

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

1

Eksiksiz bir örnek, işte React ve TypeScript ile çalışırken kullanıcı girdisi almak için benim küçük test betiğim. Kısmen diğer yorumlara ve bu bağlantıya dayanarak 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 });
        }
    });
}

}


1

TypeScript kullanıcısı için yapıcı gerekmez.

...

private divRef: HTMLDivElement | null = null

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

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

...


0

React türü tanımından

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

Böylece refs elementine aşağıdaki gibi erişebilirsiniz

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

referans indeksi yeniden tanımlanmadan.

@Manakor'un bahsettiği gibi hata alabilirsiniz.

'StepInput' özelliği '{[key: string] türünde mevcut değil: Bileşen | eleman; }

ref'leri yeniden tanımlarsanız (kullandığınız IDE ve ts sürümüne bağlıdır)


0

Sadece farklı bir yaklaşım eklemek için - ref:

let myInputElement: Element = this.refs["myInput"] as Element

0

Ben her zaman bunu yapıyorum, bu durumda bir hakem almak için

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


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

0

Eğer İletmeye alışkanlık Eğer refsen kullanılması gereken arayüz Dikmeler içinde, RefObject<CmpType>gelen tipimport React, { RefObject } from 'react';


0

Bir dizi öğeye sahip olduğunuzda nasıl yapılacağını arayanlar için:

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

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

onları bir nesneye koy


1
Lütfen cevabınızı açıklayın
AesSedai101

Bu cevap, ctrls.input'u güçlü yazılmış bir öğeye ayarlamaktır, bu da kesinlikle yazılmış bir yoldur. Bu daha iyi bir "Typcript" seçimidir.
Doug
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.