วิธีใช้การอ้างอิงในการตอบสนองกับ typescript


140

ฉันใช้ typescript กับ React ฉันมีปัญหาในการทำความเข้าใจวิธีใช้การอ้างอิงเพื่อรับการพิมพ์แบบคงที่และ Intellisense เกี่ยวกับโหนดปฏิกิริยาที่อ้างอิงโดยผู้อ้างอิง รหัสของฉันมีดังนี้

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

คำตอบ:


184

หากคุณกำลังใช้ตอบสนอง 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()ถูกเพิ่มเข้ามา


หากคุณกำลังใช้รุ่นก่อนหน้าของการตอบสนอง (<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 เทลของ ) แต่เนื่องจากบาง ประเด็น , refs สตริงเป็นกำลังใจอย่างมากและในที่สุดก็จะถูกลบออก


แก้ไขเมื่อ 22 พฤษภาคม 2018 เพื่อเพิ่มวิธีการใหม่ในการอ้างอิงใน React 16.3 ขอบคุณ @apieceofbart ที่ชี้ให้เห็นว่ามีวิธีใหม่


สังเกตว่านี่เป็นวิธีที่ต้องการ ตัวอย่างด้านล่างที่มีrefsแอตทริบิวต์คลาสจะเลิกใช้งานในเวอร์ชัน React ที่กำลังจะมีขึ้น
Jimi Pajala

1
โปรดทราบว่านี่เป็นวิธีเก่าแล้ว :) ปัจจุบันคือการใช้ React.createRef ()
apieceofbart

@apieceofbart ขอบคุณสำหรับหัว อัปเดตคำตอบเพื่อรวมวิธีใหม่
Jeff Bowen

2
ฉันไม่เห็นอะไรเกี่ยวกับ typescript ในคำตอบของคุณฉันจะเพิ่มคำตอบอื่น
apieceofbart

อ๊ะ มี typescript ในคำตอบเดิมของฉัน แต่ลืมที่จะรวมไว้ในคำตอบใหม่ เพิ่มกลับและเชื่อมโยงกับคำตอบของคุณด้วย ขอบคุณ
Jeff Bowen

30

วิธีหนึ่ง (ที่ฉันทำอยู่ ) คือการตั้งค่าด้วยตนเอง:

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

จากนั้นคุณสามารถรวมสิ่งนี้ไว้ในฟังก์ชัน getter ที่ดีกว่า (เช่นที่นี่ ):

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

1
ขอบคุณ @basarat. ฉันลองวิธีแก้ปัญหาของคุณแล้ว แต่ฉันได้รับข้อผิดพลาดนี้ 'Type Element is not assignable to type' HTMLInputElement. การยอมรับคุณสมบัติขาดหายไปในประเภท Element ''
Akshar Patel

อาจมีปัญหากับคำจำกัดความ react-dom เวอร์ชันใหม่กว่า ใช้เป็นการยืนยันในระหว่างนี้
basarat

เห็นได้ชัดว่าanyที่นี่ไม่ได้บังคับ HTMLInputElementตัวอย่างส่วนใหญ่ผมเห็นการใช้งาน เพียงแค่ระบุสิ่งที่ชัดเจน แต่ถ้าการอ้างอิงของคุณอยู่ในองค์ประกอบการตอบสนอง (กล่าวคือPeoplePicker) คุณสามารถใช้ส่วนประกอบนั้นเป็นประเภทเพื่อรับการพิมพ์
Joe Martella

24

เนื่องจาก React 16.3 วิธีเพิ่มการอ้างอิงคือใช้React.createRefตามที่ Jeff Bowen ชี้ไว้ในคำตอบของเขา อย่างไรก็ตามคุณสามารถใช้ประโยชน์จาก 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

แก้ไข: นี่ไม่ใช่วิธีที่ถูกต้องในการใช้ refs กับ 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
}

หมายเหตุ:อย่าเริ่มต้นuseRefด้วยnullในกรณีนี้ มันจะทำให้เป็น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ประเภทที่มีการทำเครื่องหมายว่าเป็นcurrent readonlyอดีตสามารถใช้ได้หากคุณต้องการเปลี่ยนโหนด DOM ปัจจุบันในการอ้างอิงตัวเองเช่นใช้ร่วมกับไลบรารีภายนอก นอกจากนี้ยังสามารถเขียนได้โดยไม่ต้อง:as useRef<HTMLInputElement | null>(null)ตัวเลือกหลังเป็นตัวเลือกที่ดีกว่าสำหรับโหนด DOM ที่มีการจัดการแบบตอบสนองตามที่ใช้ในกรณีส่วนใหญ่ React เก็บโหนดไว้ใน refs เองและคุณไม่ต้องการที่จะเปลี่ยนค่าเหล่านี้
ford04

1
ขอขอบคุณสำหรับการชี้แจง.
ksav

8

หากคุณกำลังใช้งานReact.FCให้เพิ่มHTMLDivElementอินเทอร์เฟซ:

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

และใช้ดังต่อไปนี้:

return <div ref={myRef} />;

1
ขอบคุณ เคล็ดลับสำหรับทุกคนที่เจอปัญหานี้คือการตรวจสอบองค์ประกอบ ตัวอย่างนี้อ้างถึงการใช้องค์ประกอบ DIV ตัวอย่างเช่นจะใช้ - const formRef = React.useRef <HTMLFormElement> (null);
Nick Taras

1
ขอบคุณขอบคุณขอบคุณขอบคุณขอบคุณขอบคุณขอบคุณขอบคุณขอบคุณ ขอบคุณ.
ซุ่มโจมตี

2

หากต้องการใช้รูปแบบการโทรกลับ ( https://facebook.github.io/react/docs/refs-and-the-dom.html ) ตามที่แนะนำในเอกสารของ React คุณสามารถเพิ่มคำจำกัดความสำหรับคุณสมบัติในคลาส:

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

สำหรับผู้ใช้ typescript ไม่จำเป็นต้องมีตัวสร้าง

...

private divRef: HTMLDivElement | null = null

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

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

...


0

จาก React type definition

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

ดังนั้นคุณสามารถเข้าถึงองค์ประกอบอ้างอิงของคุณได้ดังต่อไปนี้

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

โดยไม่มีการกำหนดดัชนีอ้างอิงใหม่

ตามที่ @manakor กล่าวถึงคุณจะได้รับข้อผิดพลาดเช่น

ไม่มีคุณสมบัติ 'stepInput' ในประเภท '{[key: string]: Component | ธาตุ; }

หากคุณกำหนด refs ใหม่ (ขึ้นอยู่กับรุ่น IDE และ ts ที่คุณใช้)



0

ฉันมักจะทำเช่นนี้ในกรณีนั้นเพื่อคว้าการอ้างอิง

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


ให้อินพุต: HTMLInputElement = ReactDOM.findDOMNode <HTMLInputElement> (this.refs ['input']);
user2662112

0

หากคุณไม่ต้องการส่งต่อrefในอินเทอร์เฟซ Props คุณต้องใช้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

คำตอบนี้คือการตั้งค่า ctrls.input เป็นอิลิเมนต์ที่พิมพ์มากซึ่งเป็นวิธีที่ต้องพิมพ์อย่างแรง นี่คือตัวเลือก "typescript" ที่ดีกว่า
Doug
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.