web-dev-qa-db-fra.com

Comment utiliser les références dans React avec Typescript

J'utilise TypeScript avec React. J'ai du mal à comprendre comment utiliser les références pour obtenir un typage statique et un intellisense par rapport aux noeuds de réaction référencés par les références. Mon code est comme suit.

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>
    );
}}
84
Akshar Patel

Si vous utilisez React 16.3+, la méthode suggérée pour créer des réfs utilise 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} />;
    }
}

Lorsque le composant est monté, la propriété ref de l'attribut current est affectée à l'élément composant/DOM référencé, puis de nouveau à null lors de son démontage. Ainsi, par exemple, vous pouvez y accéder en utilisant this.stepInput.current.

Pour plus d'informations sur RefObject, voir la réponse de @ apieceofbart ou le PRcreateRef() a été ajouté à.


Si vous utilisez une version antérieure de React (<16.3) ou si vous avez besoin d'un contrôle plus fin lorsque les références sont définies et non définies, vous pouvez utiliser "références de rappel" .

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

Lorsque le composant sera monté, React appellera le callback ref avec l'élément DOM et l'appelera avec null lorsqu'il sera démonté. Ainsi, par exemple, vous pouvez y accéder simplement en utilisant this.stepInput.

En définissant le callback ref comme une méthode liée sur la classe, par opposition à une fonction inline (comme dans une version précédente de cette réponse), vous pouvez éviter le rappel se faire appeler deux fois lors de mises à jour .


Il y avait une API dans laquelle l'attribut ref était une chaîne (voir la réponse de d'Akshar Patel ), mais due à certains problèmes , les références de chaîne sont fortement déconseillées et seront éventuellement supprimées.


Édité le 22 mai 2018 pour ajouter la nouvelle façon de faire les références dans React 16.3. Merci à @apieceofbart d’avoir fait remarquer qu’il existait un nouveau moyen

127
Jeff Bowen

Une façon (que je fais ) est d’installer manuellement:

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

alors vous pouvez même envelopper ceci dans une fonction de lecture plus intéressante (par exemple ici ):

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

EDIT: Ce n'est plus la bonne façon d'utiliser les références avec TypeScript. Regardez la réponse de Jeff Bowen et augmentez sa participation pour augmenter sa visibilité.

Trouvé la réponse au problème. Utilisez les références comme ci-dessous dans la classe.

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

Merci @basarat pour avoir indiqué la bonne direction.

17
Akshar Patel

Depuis React16.3, la manière d’ajouter des références consiste à utiliser React.createRef comme l’a souligné Jeff Bowen dans sa réponse. Cependant, vous pouvez tirer parti de TypeScript pour mieux taper votre ref.

Dans votre exemple, vous utilisez ref sur élément d'entrée. Donc, comme je le ferais,

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

    ...

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

En faisant cela, lorsque vous souhaitez utiliser cette référence, vous avez accès à toutes les méthodes de saisie:

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

Vous pouvez également l'utiliser sur des composants personnalisés:

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

et ensuite, par exemple, avoir accès à des accessoires:

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

Pour utiliser le style de rappel ( https://facebook.github.io/react/docs/refs-and-the-dom.html ) comme recommandé dans la documentation de React, vous pouvez ajouter la définition d'une propriété. sur la classe:

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

En l'absence d'un exemple complet, voici mon petit script de test permettant d'obtenir les entrées de l'utilisateur lorsqu'il travaille avec React et TypeScript. Basé en partie sur les autres commentaires et ce lien 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
Tikall

Si vous ne voulez pas transférer votre ref, dans l'interface des accessoires, vous devez utiliser le type RefObject<CmpType> de import React, { RefObject } from 'react';

0
MiF

Je fais toujours ça, dans ce cas pour prendre un arbitre

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

0
user2662112

De React définition du type

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

Vous pouvez donc accéder à votre élément refs comme suit

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

sans redéfinition de l'index de référence.

Comme @manakor l'a mentionné, vous pouvez obtenir une erreur comme

La propriété 'stepInput' n'existe pas sur le type '{[clé: chaîne]]: Composant | Élément; }

si vous redéfinissez les références (dépend de IDE et de la version que vous utilisez)

0

Pour l'utilisateur TypeScript, aucun constructeur n'est requis.

...

private divRef: HTMLDivElement | null = null

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

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

...

0
Morlo Mbakop

Juste pour ajouter une approche différente - vous pouvez simplement lancer votre ref, quelque chose comme:

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