如何在 React 和 Typescript 中使用 refs

IT技术 reactjs typescript
2021-05-01 06:14:08

我在 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>
    );
}}
4个回答

如果您使用的是 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 的回答添加了 PR createRef()


如果您使用的是较早版本的 React (<16.3) 或需要更细粒度地控制何时设置和取消设置 refs,则可以使用“回调 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} />
    }
}

当组件挂载时,React 将ref使用 DOM 元素调用回调,并null在卸载时调用它因此,例如,您只需使用this.stepInput.

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


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


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

React.createRef (class)

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

inputRef.currentreadonly通过用 初始化其值成为一个属性null

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

注意:在这种情况下不要初始化useRefwith null- 它会生成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} />
}

注意:字符串引用被认为是遗留的,在本答案的范围内被省略。

游乐场示例

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

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

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

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

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

在您的示例中,您在输入元素上使用 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>>;

然后,例如,访问 props :

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