Bagaimana cara menggunakan React.forwardRef dalam komponen berbasis kelas?

113

Saya mencoba menggunakan React.forwardRef, tetapi tersandung cara membuatnya bekerja dalam komponen berbasis kelas (bukan HOC).

Contoh dokumen menggunakan elemen dan komponen fungsional, bahkan membungkus kelas dalam fungsi untuk komponen tingkat tinggi.

Jadi, mulailah dengan sesuatu seperti ini di ref.jsfile mereka :

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

dan sebagai gantinya mendefinisikannya sebagai sesuatu seperti ini:

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

atau

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

hanya bekerja: /

Juga, saya tahu saya tahu, wasit bukanlah cara bereaksi. Saya mencoba menggunakan pustaka kanvas pihak ketiga, dan ingin menambahkan beberapa alat mereka dalam komponen terpisah, jadi saya membutuhkan pendengar acara, jadi saya memerlukan metode siklus hidup. Ini mungkin pergi ke rute yang berbeda nanti, tetapi saya ingin mencoba ini.

Dokumen mengatakan itu mungkin!

Penerusan referensi tidak terbatas pada komponen DOM. Anda juga dapat meneruskan ref ke instance komponen kelas.

dari catatan di bagian ini.

Tapi kemudian mereka terus menggunakan HOC, bukan hanya kelas.

Han Lazarus
sumber

Jawaban:

108

Ide untuk selalu menggunakan prop yang sama untuk refdapat dicapai dengan proxy class export dengan helper.

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

Jadi pada dasarnya, ya, kami dipaksa untuk memiliki prop yang berbeda untuk meneruskan ref, tetapi itu bisa dilakukan di bawah hub. Penting bagi publik untuk menggunakannya sebagai referensi normal.

Bapak Br
sumber
4
Apa yang Anda lakukan jika Anda memiliki komponen yang dibungkus dalam HOC seperti React-Redux connectatau marterial-ui withStyles?
J. Hesters
Apa masalahnya dengan connectatau withStyles? Anda harus membungkus semua HOC dengan forwardRefdan secara internal menggunakan prop "aman" untuk meneruskan referensi ke komponen terendah dalam rantai.
Tuan Br
Adakah wawasan tentang cara menguji ini di Enzim saat Anda menggunakan setState?
zero_cool
Maaf, saya perlu detail lebih lanjut. Tidak yakin apa masalahnya sebenarnya.
Tuan Br
2
Saya mendapatkan: Pelanggaran Invarian: Objek tidak valid sebagai anak React (ditemukan: objek dengan kunci {$$ typeof, render}). Jika Anda bermaksud merender koleksi anak-anak, gunakan array sebagai gantinya.
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()} />
)

Anda perlu menggunakan nama prop yang berbeda untuk meneruskan referensi ke kelas. innerRefbiasanya digunakan di banyak perpustakaan.

Sebastien Lorber
sumber
dalam kode Anda beautifulInputElementlebih merupakan fungsi daripada Elemen Bereaksi, seharusnya seperti iniconst beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
Olivier Boissé
8

Pada dasarnya, ini hanyalah fungsi HOC. Jika Anda ingin menggunakannya dengan kelas, Anda dapat melakukannya sendiri dan menggunakan alat peraga biasa.

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

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

Pola ini digunakan misalnya di styled-componentsdan disebut di innerRefsana.

Łukasz Wojciechowski
sumber
3
Menggunakan prop dengan nama berbeda seperti innerRefbenar-benar kehilangan intinya. Tujuannya adalah transparansi lengkap: Saya harus dapat memperlakukan <FancyButton>seolah-olah itu adalah elemen DOM biasa, tetapi menggunakan pendekatan komponen bergaya, saya perlu ingat bahwa komponen ini menggunakan prop yang dinamai sewenang-wenang untuk ref, bukan hanya ref.
pengguna128216
2
Dalam kasus apa pun, komponen bergaya bermaksud untuk menjatuhkan dukungan untukinnerRef meneruskan referensi ke anak menggunakan React.forwardRef, itulah yang OP ingin capai.
pengguna128216
5

Ini dapat dilakukan dengan komponen tingkat tinggi, jika Anda suka:

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

Dan kemudian di file komponen Anda:

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

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

export default withForwardedRef(Boop)

Saya melakukan dimuka bekerja dengan tes & menerbitkan paket untuk ini, react-with-forwarded-ref: https://www.npmjs.com/package/react-with-forwarded-ref

rpearce
sumber
3

Jika Anda perlu menggunakan kembali ini di banyak komponen yang berbeda, Anda dapat mengekspor kemampuan ini ke sesuatu seperti withForwardingRef

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

export default withForwardingRef;

pemakaian:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef
orepor
sumber