如何在带有Typescript的React中使用refs


139

我在React中使用Typescript。我在理解如何使用refs方面遇到麻烦,以便相对于refs引用的react节点获得静态类型和智能感知。我的代码如下。

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

Answers:


183

如果您使用的是React 16.3+,建议的创建引用的方法是使用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} />;
    }
}

组件安装后,ref属性的current属性将分配给引用的组件/ DOM元素,并null在卸载时分配回该属性。因此,例如,您可以使用进行访问this.stepInput.current

有关更多信息RefObject,请参见@apieceofbart的答案或添加了PR createRef()


如果您使用的是较早版本的React(<16.3)或需要对引用的设置和取消设置进行更细粒度的控制,则可以使用“回调引用”

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

当组件挂载时,React将ref使用DOM元素调用回调,并null在卸载时使用DOM元素调用回调。因此,例如,您可以使用轻松访问它this.stepInput

通过将ref回调定义为类的绑定方法,而不是内联函数(如先前版本)如此答案),可以避免更新过程中两次调用该回调。


曾经是一个API,其中ref属性是一个字符串(见AKSHAR特尔的答案),但由于一些 问题,串裁判的强烈反对,并最终将被删除。


编辑于2018年5月22日,以在React 16.3中添加执行引用的新方法。感谢@apieceofbart指出有一种新方法。


请注意,这是首选方式。以下带有refsclass属性的示例将在即将发布的React版本中弃用。
Jimi Pajala

1
请注意,这已经是一种旧方法了:)当前是使用React.createRef()
apieceofbart

@apieceofbart感谢您的注意。更新了答案以包括新方法。
杰夫·鲍文

2
我只是在您的答案中什么都没有看到打字稿,我将添加另一个答案
apieceofbart

哎呀 我原来的答案中有打字稿,但忘了将它包含在新的答案中。加回去,并链接到您的答案。谢谢。
杰夫·鲍文

30

一种方法(我一直在做)是手动设置:

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

那么您甚至可以将其包装在一个更好的getter函数中(例如here):

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

1
谢谢@basarat。我尝试了您的解决方案,但出现此错误“类型元素无法分配给类型” HTMLInputElement。元素类型缺少属性接受”
Akshar Patel 2015年

较新版本的react-dom定义可能会出现问题。同时用作断言
basarat 2015年

显然any这里不是强制性的。我看到的大多数示例都可以使用HTMLInputElement。仅说明显而易见的内容,但如果您的引用位于React组件(即PeoplePicker)上,则可以将该组件用作获取类型的类型。
Joe Martella

23

从React 16.3开始,添加引用的方法是使用Jeff Bowen在回答中指出的React.createRef。但是,您可以利用Typescript更好地键入您的引用。

在您的示例中,您在输入元素上使用了ref。因此,他们这样做的方式是:

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

    ...

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

通过在希望使用该引用的情况下执行此操作,可以访问所有输入法:

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

您也可以在自定义组件上使用它:

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

然后可以访问道具:

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

17

编辑:这已不再是使用Typescript引用的正确方法。查看Jeff Bowen的答案,并对其进行投票以提高其可见度。

找到了问题的答案。在类中使用下面的引用。

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

感谢@basarat指出正确的方向。


2
Property 'stepInput' does not exist on type '{ [key: string]: Component<any, any> | Element; }'尝试访问时,我仍在获取信息this.refs.stepInput
Nik Sumeiko

@NikSumeiko,您收到该错误,因为您的refs对象只有该[key: string]条目。
Joe Martella

9

React.createRef (类组件)

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

注意:此处省略旧的String Refs旧版API。


React.useRef (挂钩/功能组件)

DOM节点的只读引用:
const FunctionApp = () => {
  const inputRef = React.useRef<HTMLInputElement>(null) // note the passed in `null` arg
  return <input type="text" ref={inputRef} />
}
任意存储值的可变引用
const FunctionApp = () => {
  const renderCountRef = useRef(0)
  useEffect(() => {
    renderCountRef.current += 1
  })
  // ... other render code
}

注意:不要初始化useRefnull在这种情况下。它将成为renderCountRef类型readonly(请参阅示例)。如果您需要提供null初始值,请执行以下操作:

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

回调参考(两者均适用)

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

游乐场样本


useRef() as MutableRefObject<HTMLInputElement>和之间有什么区别useRef<HTMLInputElement>(null)
ksav

2
好的问题-的current属性MutableRefObject<HTMLInputElement>可以修改,而useRef<HTMLInputElement>(null)创建一个标记为的RefObject类型。如果您需要自己更改引用中的当前DOM节点(例如与外部库结合使用),则可以使用前者。也可以在没有:的情况下编写。在大多数情况下,后者是React受管DOM节点的更好选择。React将节点存储在ref本身中,您不想摸索更改这些值。currentreadonlyasuseRef<HTMLInputElement | null>(null)
ford04

1
感谢您的澄清。
ksav

7

如果您使用React.FC,请添加HTMLDivElement接口:

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

并按如下所示使用它:

return <div ref={myRef} />;

1
谢谢。任何遇到此问题的人的另一个提示是检查Element。本示例涉及DIV元素的用法。例如,一个表单将使用-const formRef = React.useRef <HTMLFormElement>(null);
尼克塔拉斯

1
谢谢谢谢谢谢谢谢谢谢谢谢谢谢 谢谢。
Ambrown,

2

要按照React文档的建议使用回调样式(https://facebook.github.io/react/docs/refs-and-the-dom.html),您可以在类的属性上添加一个定义:

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

缺少完整的示例,这是我的小测试脚本,用于在使用React和TypeScript时获取用户输入。部分基于其他评论以及此链接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

对于打字稿用户,不需要构造函数。

...

private divRef: HTMLDivElement | null = null

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

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

...


0

从React类型定义

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

因此,您可以按以下方式访问refs元素

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

没有重新定义refs索引。

正如@manakor提到的那样,您会得到类似的错误

类型'{[键:字符串]:属性| 元件; }

如果您重新定义引用(取决于您使用的IDE和ts版本)



0

我总是这样做,在这种情况下要获取参考

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


让输入:HTMLInputElement = ReactDOM.findDOMNode <HTMLInputElement>(this.refs ['input']);
user2662112 '17

0

如果你不会转发您的ref界面,你需要使用,道具RefObject<CmpType>从类型import React, { RefObject } from 'react';


0

对于那些希望在拥有一系列元素的情况下如何做的人:

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

把它们放在一个物体上


1
请解释您的答案
AesSedai101 '16

这个答案是将ctrls.input设置为强类型元素,这是采用强类型的方式。这是更好的“ Typescript”选择。
Doug
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.