จะใช้ React.forwardRef ในองค์ประกอบตามคลาสได้อย่างไร


115

ฉันพยายามใช้ React.forwardRef แต่สะดุดวิธีทำให้มันทำงานในองค์ประกอบตามคลาส (ไม่ใช่ HOC)

ตัวอย่างเอกสารใช้องค์ประกอบและส่วนประกอบที่ใช้งานได้แม้กระทั่งการตัดคลาสในฟังก์ชันสำหรับส่วนประกอบลำดับที่สูงกว่า

เริ่มต้นด้วยสิ่งนี้ในref.jsไฟล์:

const TextInput = React.forwardRef(
    (props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />)
);

และแทนที่จะกำหนดเป็นดังนี้:

class TextInput extends React.Component {
  render() {
    let { props, ref } = React.forwardRef((props, ref) => ({ props, ref }));
    return <input type="text" placeholder="Hello World" ref={ref} />;
  }
}

หรือ

class TextInput extends React.Component {
  render() { 
    return (
      React.forwardRef((props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />))
    );
  }
}

ใช้งานได้เท่านั้น: /

นอกจากนี้ฉันรู้ว่าฉันรู้ว่า ref ไม่ใช่วิธีตอบสนอง ฉันกำลังพยายามใช้ไลบรารีแคนวาสของบุคคลที่สามและต้องการเพิ่มเครื่องมือบางอย่างในส่วนประกอบแยกต่างหากดังนั้นฉันต้องการตัวฟังเหตุการณ์ดังนั้นฉันจึงต้องการวิธีวงจรชีวิต มันอาจไปเส้นทางอื่นในภายหลัง แต่ฉันอยากลองทำเช่นนี้

เอกสารบอกว่าเป็นไปได้!

การส่งต่อ Ref ไม่ จำกัด เฉพาะคอมโพเนนต์ DOM คุณสามารถส่งต่อการอ้างอิงไปยังอินสแตนซ์ส่วนประกอบของคลาสได้เช่นกัน

จากหมายเหตุในส่วนนี้

แต่แล้วพวกเขาก็ใช้ HOCs แทนการเรียน

คำตอบ:


110

ความคิดที่จะใช้เสาเดียวกันเสมอสำหรับสิ่งที่refสามารถทำได้โดยการส่งออกคลาสพร็อกซีด้วยตัวช่วย

class ElemComponent extends Component {
  render() {
    return (
      <div ref={this.props.innerRef}>
        Div has ref
      </div>
    )
  }
}

export default React.forwardRef((props, ref) => <ElemComponent 
  innerRef={ref} {...props}
/>);

ดังนั้นโดยพื้นฐานแล้วเราถูกบังคับให้มีเสาอื่นเพื่อส่งต่อการอ้างอิง แต่สามารถทำได้ภายใต้ฮับ สิ่งสำคัญคือให้ประชาชนใช้เป็นเอกสารอ้างอิงปกติ


4
คุณจะทำอย่างไรหากคุณมีส่วนประกอบของคุณอยู่ในห่อ HOC เช่น React-Redux ของconnectหรือ Marterial-UI เป็นwithStyles?
J. Hesters

มีปัญหาอะไรconnectหรือwithStyles? คุณควรห่อ HOC ทั้งหมดด้วยforwardRefและใช้เสา "ปลอดภัย" ภายในเพื่อส่งต่อการอ้างอิงไปยังส่วนประกอบที่ต่ำที่สุดในห่วงโซ่
Mr Br

มีข้อมูลเชิงลึกเกี่ยวกับวิธีทดสอบสิ่งนี้ใน Enzyme เมื่อคุณใช้ setState หรือไม่?
zero_cool

ขออภัยฉันต้องการรายละเอียดเพิ่มเติมเล็กน้อย ไม่แน่ใจว่าเป็นปัญหาอะไรกันแน่
Mr Br

2
ฉันได้รับ: Invariant Violation: Objects ไม่ถูกต้องเป็น React child (found: object with keys {$$ typeof, render}) หากคุณต้องการสร้างคอลเลกชันเด็กให้ใช้อาร์เรย์แทน
Brian Loughnane

9
class BeautifulInput extends React.Component {
  const { innerRef, ...props } = this.props;
  render() (
    return (
      <div style={{backgroundColor: "blue"}}>
        <input ref={innerRef} {...props} />
      </div>
    )
  )
}

const BeautifulInputForwardingRef = React.forwardRef((props, ref) => (
  <BeautifulInput {...props} innerRef={ref}/>
));

const App = () => (
  <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
)

คุณต้องใช้ชื่อเสาอื่นเพื่อส่งต่อการอ้างอิงไปยังคลาส innerRefมักใช้ในห้องสมุดหลายแห่ง


ในโค้ดของคุณbeautifulInputElementเป็นฟังก์ชันมากกว่า React Element มันควรจะเป็นแบบนี้const beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
Olivier Boissé

8

โดยทั่วไปนี่เป็นเพียงฟังก์ชัน HOC หากคุณต้องการใช้กับชั้นเรียนคุณสามารถทำได้ด้วยตัวเองและใช้อุปกรณ์ประกอบฉากปกติ

class TextInput extends React.Component {
    render() {
        <input ref={this.props.forwardRef} />
    }
}

const ref = React.createRef();
<TextInput forwardRef={ref} />

รูปแบบนี้ใช้เป็นตัวอย่างstyled-componentsและเรียกว่าinnerRefมี


3
การใช้ไม้ค้ำยันที่มีชื่อแตกต่างกันinnerRefนั้นขาดประเด็นไปอย่างสิ้นเชิง เป้าหมายของเราคือโปร่งใสสมบูรณ์: ผมควรจะสามารถรักษา<FancyButton>ราวกับว่ามันเป็นองค์ประกอบ DOM ปกติ แต่ใช้ส่วนประกอบที่มีสไตล์เข้าใกล้ผมต้องจำไว้ว่าส่วนนี้ใช้ชื่อพลเสาสำหรับ refs refแทนเพียง
user128216

2
ไม่ว่าในกรณีใดส่วนประกอบที่มีสไตล์ตั้งใจที่จะลดการสนับสนุนเพื่อinnerRefส่งต่อการอ้างอิงไปยังเด็กโดยใช้React.forwardRefซึ่งเป็นสิ่งที่ OP พยายามทำให้สำเร็จ
user128216

5

สิ่งนี้สามารถทำได้โดยใช้องค์ประกอบลำดับที่สูงขึ้นหากคุณต้องการ:

import React, { forwardRef } from 'react'

const withForwardedRef = Comp => {
  const handle = (props, ref) =>
    <Comp {...props} forwardedRef={ref} />

  const name = Comp.displayName || Comp.name
  handle.displayName = `withForwardedRef(${name})`

  return forwardRef(handle)
}

export default withForwardedRef

จากนั้นในไฟล์คอมโพเนนต์ของคุณ:

class Boop extends React.Component {
  render() {
    const { forwardedRef } = this.props

    return (
      <div ref={forwardedRef} />
    )
  }
}

export default withForwardedRef(Boop)

ฉันได้ทำการทดสอบล่วงหน้าและเผยแพร่แพ็คเกจสำหรับสิ่งนี้react-with-forwarded-ref: https://www.npmjs.com/package/react-with-forwarded-ref


3

ในกรณีที่คุณต้องใช้สิ่งนี้ซ้ำในส่วนประกอบที่แตกต่างกันคุณสามารถส่งออกความสามารถนี้ไปยังสิ่งที่ต้องการได้ withForwardingRef

const withForwardingRef = <Props extends {[_: string]: any}>(BaseComponent: React.ReactType<Props>) =>
    React.forwardRef((props, ref) => <BaseComponent {...props} forwardedRef={ref} />);

export default withForwardingRef;

การใช้งาน:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.