Cách sử dụng ref trong React with Typecript


139

Tôi đang sử dụng Typecript với React. Tôi gặp khó khăn trong việc hiểu cách sử dụng ref để có được kiểu gõ tĩnh và intellisense đối với các nút phản ứng được tham chiếu bởi các ref. Mã của tôi là như sau.

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

Câu trả lời:


183

Nếu bạn đang sử dụng React 16.3+, cách được đề xuất để tạo ref là sử dụng 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} />;
    }
}

Khi thành phần gắn kết, refthuộc tính của thuộc tính currentsẽ được gán cho thành phần thành phần / DOM được tham chiếu và được gán lại nullkhi nó ngắt kết nối. Vì vậy, ví dụ, bạn có thể truy cập nó bằng cách sử dụng this.stepInput.current.

Để biết thêm về RefObject, hãy xem câu trả lời của @ apieceofbart hoặc PR createRef() đã được thêm vào.


Nếu bạn đang sử dụng phiên bản React trước đó (<16.3) hoặc cần kiểm soát chi tiết hơn đối với khi giới thiệu được đặt và không được đặt, bạn có thể sử dụng giới thiệu gọi lại giới thiệu .

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

Khi thành phần gắn kết, React sẽ gọi reflại cuộc gọi với phần tử DOM và sẽ gọi nó nullkhi nó ngắt kết nối. Vì vậy, ví dụ, bạn có thể truy cập nó đơn giản bằng cách sử dụng this.stepInput.

Bằng cách định nghĩa cuộc refgọi lại là một phương thức ràng buộc trên lớp trái ngược với hàm nội tuyến (như trong phiên bản trước của câu trả lời này), bạn có thể tránh cuộc gọi lại được gọi hai lần trong khi cập nhật.


Đã từng có một API trong đó refthuộc tính là một chuỗi (xem câu trả lời của Akshar Patel ), nhưng do một số vấn đề , các tham chiếu chuỗi bị ngăn cản mạnh mẽ và cuối cùng sẽ bị xóa.


Đã chỉnh sửa ngày 22 tháng 5 năm 2018 để thêm cách làm mới refs trong React 16.3. Cảm ơn @apieceofbart đã chỉ ra rằng có một cách mới.


Lưu ý đây là cách ưa thích. Các ví dụ dưới đây với refsthuộc tính lớp sẽ không được chấp nhận trong các phiên bản React sắp tới.
Jimi Pajala

1
Xin lưu ý rằng đây đã là một cách cũ :) hiện tại là sử dụng React.createRef ()
apieceofbart

@apieceofbart Cảm ơn những người đứng đầu. Cập nhật câu trả lời để bao gồm cách mới.
Jeff Bowen

2
Tôi chỉ không thấy bất cứ điều gì về bản thảo trong câu trả lời của bạn, tôi sẽ thêm một câu trả lời khác
apieceofbart

Rất tiếc. Có bản đánh máy trong câu trả lời ban đầu của tôi nhưng quên đưa nó vào câu trả lời mới. Đã thêm nó trở lại và liên kết với câu trả lời của bạn là tốt. Cảm ơn.
Jeff Bowen

30

Một cách (mà tôi đã làm ) là thiết lập thủ công:

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

sau đó bạn thậm chí có thể gói nó trong một hàm getter đẹp hơn (ví dụ ở đây ):

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

1
Cảm ơn @basarat. Tôi đã thử giải pháp của bạn nhưng tôi nhận được lỗi này 'Loại phần tử không thể gán cho loại' HTMLInputEuity. Chấp nhận tài sản bị thiếu trong loại Element ''
Akshar Patel

Có thể là một vấn đề với phiên bản mới hơn của các định nghĩa Reac-dom. Sử dụng như khẳng định trong thời gian này
basarat

Rõ ràng anylà không bắt buộc ở đây. Hầu hết các ví dụ tôi thấy sử dụng HTMLInputElement. Chỉ cần nêu rõ ràng, nhưng nếu ref của bạn nằm trên thành phần React (nghĩa là PeoplePicker), bạn có thể sử dụng thành phần đó làm kiểu để lấy các kiểu chữ.
Joe Martella

23

Vì React 16.3, cách để thêm ref là sử dụng React.createRef như Jeff Bowen đã chỉ ra trong câu trả lời của mình. Tuy nhiên, bạn có thể tận dụng Bản mô tả để nhập tốt hơn tài liệu tham khảo của mình.

Trong ví dụ của bạn, bạn đang sử dụng ref trên phần tử đầu vào. Vì vậy, cách họ sẽ làm là:

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

    ...

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

Bằng cách này, khi bạn muốn sử dụng ref đó, bạn có quyền truy cập vào tất cả các phương thức nhập liệu:

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

Bạn cũng có thể sử dụng nó trên các thành phần tùy chỉnh:

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

và sau đó, ví dụ, có quyền truy cập vào đạo cụ:

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

17

EDIT: Đây không còn là cách đúng đắn để sử dụng refs với Typecript. Nhìn vào câu trả lời của Jeff Bowen và nâng cao nó để tăng khả năng hiển thị của nó.

Tìm thấy câu trả lời cho vấn đề. Sử dụng ref như dưới đây trong lớp.

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

Cảm ơn @basarat đã chỉ đúng hướng.


2
Tôi vẫn nhận được Property 'stepInput' does not exist on type '{ [key: string]: Component<any, any> | Element; }', khi cố gắng truy cập this.refs.stepInput.
Nik Sumeiko

@NikSumeiko, bạn đã gặp phải lỗi đó vì refsđối tượng của bạn chỉ có [key: string]mục.
Joe Martella

8

React.createRef (thành phần lớp)

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

Lưu ý: Bỏ API kế thừa của Chuỗi giới thiệu cũ tại đây ...


React.useRef (Móc / thành phần chức năng)

Giới thiệu chỉ đọc cho các nút DOM:
const FunctionApp = () => {
  const inputRef = React.useRef<HTMLInputElement>(null) // note the passed in `null` arg
  return <input type="text" ref={inputRef} />
}
Refs có thể thay đổi cho các giá trị được lưu trữ tùy ý:
const FunctionApp = () => {
  const renderCountRef = useRef(0)
  useEffect(() => {
    renderCountRef.current += 1
  })
  // ... other render code
}

Lưu ý: Đừng khởi tạo useRefvới nulltrong trường hợp này. Nó sẽ làm cho renderCountRefloại readonly(xem ví dụ ). Nếu bạn cần cung cấp nullnhư giá trị ban đầu, hãy làm điều này:

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

Gọi lại giới thiệu (làm việc cho cả hai)

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

Mẫu sân chơi


Sự khác biệt giữa useRef() as MutableRefObject<HTMLInputElement>và là useRef<HTMLInputElement>(null)gì?
ksav

2
Câu hỏi hay - thuộc currenttính của MutableRefObject<HTMLInputElement>có thể được sửa đổi, trong khi useRef<HTMLInputElement>(null)tạo ra một RefObjectloại có currentđánh dấu là readonly. Cái trước có thể được sử dụng, nếu bạn cần thay đổi các nút DOM hiện tại trong chính refs, ví dụ như kết hợp với một thư viện bên ngoài. Nó cũng có thể được viết mà không cần as: useRef<HTMLInputElement | null>(null). Cái sau là một lựa chọn tốt hơn cho các nút DOM được quản lý React, như được sử dụng trong hầu hết các trường hợp. React lưu trữ các nút trong chính refs và bạn không muốn tìm hiểu về việc thay đổi các giá trị này.
ford04

1
Cảm ơn bạn đã làm rõ.
ksav

7

Nếu bạn đang sử dụng React.FC, hãy thêm HTMLDivElementgiao diện:

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

Và sử dụng nó như sau:

return <div ref={myRef} />;

1
Cảm ơn. Một mẹo khác cho bất kỳ ai đi qua đây là kiểm tra Element. Ví dụ này đề cập đến việc sử dụng phần tử DIV. Ví dụ, một biểu mẫu sẽ sử dụng - const formRef = React.useRef <HTMLFormEuity> (null);
Nick Taras

1
Cảm ơn bạn cảm ơn bạn cảm ơn bạn cảm ơn bạn cảm ơn bạn cảm ơn bạn cảm ơn bạn cảm ơn bạn. Cảm ơn bạn.
Ambrown

2

Để sử dụng kiểu gọi lại ( https://facebook.github.io/react/docs/refs-and-the-dom.html ) như được đề xuất trên tài liệu của React, bạn có thể thêm định nghĩa cho thuộc tính trên lớp:

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

Thiếu một ví dụ hoàn chỉnh, đây là kịch bản thử nghiệm nhỏ của tôi để nhận đầu vào của người dùng khi làm việc với React và TypeScript. Dựa một phần vào các bình luận khác và liên kết này 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

Đối với người dùng bản in không cần xây dựng.

...

private divRef: HTMLDivElement | null = null

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

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

...


0

Từ định nghĩa kiểu React

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

Vì vậy, bạn có thể truy cập vào phần tử ref của bạn như sau

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

mà không xác định lại chỉ số refs.

Như @manakor đã đề cập, bạn có thể gặp lỗi như

Thuộc tính 'stepInput' không tồn tại trên loại '{[key: string]: Thành phần | Thành phần; }

nếu bạn xác định lại refs (phụ thuộc vào phiên bản IDE và ts bạn sử dụng)


0

Chỉ cần thêm một cách tiếp cận khác - bạn có thể chỉ cần sử dụng ref của mình, đại loại như:

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

0

Tôi luôn luôn làm điều này, trong trường hợp đó để lấy một ref

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


hãy nhập: HTMLInputEuity = ReactDOM.findDOMNode <HTMLInputEuity> (this.refs ['input']);
user2662112

0

Nếu bạn không muốn chuyển tiếp ref, trong giao diện Đạo cụ, bạn cần sử dụng RefObject<CmpType>loại từimport React, { RefObject } from 'react';


0

Đối với những người tìm kiếm cách làm điều đó khi bạn có một loạt các yếu tố:

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

đặt chúng vào một vật


1
Vui lòng giải thích câu trả lời của bạn
AesSedai101

Câu trả lời này là đặt ctrls.input thành một phần tử được gõ mạnh, đây là cách được gõ mạnh. Đây là một sự lựa chọn "Bản đánh máy" tốt hơn.
Doug
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.