如何使用refs在React与Typescript

我使用Typescript与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>
);
}}
386122 次浏览

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

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

那么你甚至可以用一个更好的getter函数(例如在这里)来包装它:

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

编辑:这不再是在Typescript中使用refs的正确方式。看看杰夫·鲍恩的回答,给它点赞,以提高它的曝光度。

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

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

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

由于缺乏完整的示例,下面是我的小测试脚本,用于在使用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 });
}
});
}

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

把它们放在一个对象中

来自React类型定义

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

你可以像下面这样访问你的refs元素

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

没有重新定义refs索引。

正如@manakor提到的,你可能会得到这样的错误

属性“stepInput”在类型上不存在{[key: string]: 组件|元素;} < / p >

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

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

只是添加一个不同的方法-你可以简单地强制转换你的引用,像这样:

let myInputElement: Element = this.refs["myInput"] as Element
在这种情况下,我总是这样做 获取一个ref

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

如果你使用React 16.3+, 建议的方法创建refs是使用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的回答公关 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将使用DOM元素调用ref回调,并在卸载时使用null调用它。因此,例如,您可以简单地使用this.stepInput访问它。

通过将ref回调定义为类上的绑定方法,而不是内联函数(如在此answer的之前的版本中),可以避免回调期间被呼叫两次更新。


有一个曾经是 API,其中ref属性是一个字符串(参见阿克沙尔·帕特尔的回答是),但由于一些 问题,字符串引用是强烈反对的,最终将被删除。


编辑于2018年5月22日,在React 16.3中添加了新的裁判方式。感谢@apieceofbart指出了一种新方法。

自React 16.3以来,添加refs的方法是使用React.createRef,正如Jeff Bowen在他的回答中指出的那样。然而,你可以利用Typescript来更好地输入你的引用。

在你的例子中,你在input元素上使用了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} />;
}
}

通过这样做,当你想使用那个ref时,你可以访问所有的输入法:

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

对于typescript用户,不需要构造函数。

...

private divRef: HTMLDivElement | null = null


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


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

...

如果你不愿意转发你的ref,在Props界面中你需要使用import React, { RefObject } from 'react';中的RefObject<CmpType>类型

如果你正在使用React.FC,添加HTMLDivElement接口:

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

然后像下面这样使用它:

return <div ref={myRef} />;

当你有一个元素数组的时候,你可以这样做:

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

React.createRef(类比较)

class ClassApp extends React.Component {
inputRef = React.createRef<HTMLInputElement>();
  

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

React.useRef(钩子/函数比较)

a)对react管理的DOM节点使用只读引用:
const FunctionApp = () => {
// note the passed-in `null` arg ----------------v
const inputRef = React.useRef<HTMLInputElement>(null)
return <input type="text" ref={inputRef} />
}

通过使用null初始化其值,inputRef.current成为readonly属性。

b)对类似于实例变量的任意存储值使用可变参:
const FunctionApp = () => {
const renderCountRef = useRef(0)
useEffect(() => {
renderCountRef.current += 1
})
// ... other render code
}

注意:在这种情况下,不要用null初始化useRef——它将使renderCountRef类型为readonly(参见例子)。如果你需要提供null作为初始值,这样做:

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

回调引用(两个)

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

注意:字符串参被认为是遗留的,在这个答案的范围内被省略。

<一个href = " https://www.typescriptlang.org/play?代码/ JYWwDg9gTgLgBAJQKYEMDGMA0cDecCuAzkgKIBmZSGcAvnGVBCHAERSoYsCwAULwPT84AQQA2MJFAB2KGMABuSAFwChoSLEQcsuOGnaykyMtiKkKVHWeO16jZm23ceguADoPcFFIAmcdmCi6Eha6DBu1khkcAC0AHwExMbYyGFu + qgSNvF6BllRvKqqoRjpeUZRcAAUaEGEhHpMYG4AlEU8tSj1cADCdYTCYGBwSAAeEr4NqaU9TRBSSFLwOLxwa3DAUmD4MDYAvCXhGYbGADwAEgAqALIAMgCSWzskokggizBxVW18POtwAAtvD5XgARJgAOQgPiQPSBUgA5iEDlUfJDoco4Fc7o9tjAXm8PnAAD5wKT4USiFpwPYJFZ-f7rVxouCEJhIGAAzYIuAAd2AnL08KRfjRzCkGLcq3WNEKDLW7F8km + uGljP8HPw0jgpziavV61OmzxcBgAE8wEg9iwJOMWBqyHscJzgIQ3MadsY6Pw9fKDWsjU94ObLdbbTB7exHc6uW74SCkOCQFCYXDvEjvb7-acffrZTx8 + 1 xnnwpfolvzhaiabrbpcmj4kqyydzrrgvrtndzqjwabijeb80gwxr1npqr9rhq1imakkojndoazggcfbahlrmsmyr9v2pd2nkh9ocikkohcbg8gwt3ksvutkt99a4aebuka7fcv15l + irlAoFfAUoEAxce2gJA-HkFBRHwJBCCUOA927eA2XeAA1WD4JPEszwqMgqgABh + f4zHISgMCqFVx3pf10KQLC4II9ItUVeAAGoDgARjzbAAG0AF1nz9dgYC1KQdR8BQswNQMTRDK0bTGCMHSdD1diiTN9X + U5pPkOJkCVKBGkbGAkJwBimJwrSc302SA34ezeELFx + GKHpYNEV90GrP8qlfCBOWpGJAUkEJ + UFBsmzkVsu3AeYiU7VDei8ny0GrYcaWqMc6RQg94HjMF0VTYVkWqMUU0xbFrzxW8iVJR8qRpPK-X+VwPDcOAWQYl1ET5AUASFdMoO6phtxhKU-Xzf4xIknUNNNC0lPDSMoidIrExK2Eyu0gs5SAA" rel="noreferrer">Playground sample

FIRST ADD AN IMPORT

import React, { useRef } from "react";

那么这个

const studentCapacityRef = useRef<HTMLInputElement>(null);

或者这个

const studentCapacityRef = useRef<HTMLAreaElement>(null);

或者这个

const studentCapacityRef = useRef<HTMLDivElement>(null);

等等……