Forwarding Refs

Ref forwarding adalah sebuah teknik untuk meneruskan ref secara otomatis melalui komponen ke salah satu anaknya. Ini biasanya tidak diperlukan untuk sebagian besar komponen dalam aplikasi. Namun, ini bisa berguna untuk beberapa jenis komponen, terutama di pustaka komponen yang dapat digunakan kembali. Skenario paling umum dijelaskan di bawah ini.

Mengoper refs ke komponen DOM

Pertimbangkan sebuah komponen FancyButton yang me-render native DOM element yaitu button:

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}

Komponen React menyembunyikan detail implementasi mereka, termasuk keluaran render mereka. Komponen-komponen lainnya yang menggunakan FancyButton biasanya tidak perlu untuk mendapatkan sebuah ref ke dalam DOM element button. Ini bagus karena hal ini mencegah komponen untuk bersandar pada struktur DOM komponen lain yang berlebihan.

Meskipun enkapsulasi seperti itu diperlukan oleh komponen yang berada di application-level seperti FeedStory atau Comment, namun hal tersebut dapat menyulitkan komponen-komponen “leaf” yang sering digunakan kembali seperti FancyButton atau MyTextInput. Komponen ini cenderung digunakan di seluruh aplikasi dengan cara yang sama seperti regular DOM button dan input, dan mungkin tidak dapat dihindari mengakses DOM nodes mereka untuk mengelola focus, selection, atau animation.

Ref forwarding adalah sebuah fitur keikutsertaan yang memperbolehkan beberapa komponen-komponen mengambil sebuah ref yang didapatkan, dan menurunkannya (dengan kata lain, “meneruskan” nya) kepada child.

Pada contoh di bawah ini, FancyButton menggunakan React.forwardRef untuk mendapatkan ref yang diteruskan kepadanya, lalu meneruskannya ke button DOM yang di render:

const FancyButton = React.forwardRef((props, ref) => (  <button ref={ref} className="FancyButton">    {props.children}
  </button>
));

// Sekarang Anda bisa mendapatkan ref langsung ke button DOM:
const ref = React.createRef();
<FancyButton ref={ref}>Klik saya!</FancyButton>;

Dengan cara ini, komponen-komponen yang menggunakan FancyButton bisa mendapatkan ref ke DOM node button yang mendasarinya dan mengaksesnya jika perlu - sama seperti jika mereka menggunakan button DOM secara langsung.

Berikut adalah penjelasan langkah demi langkah tentang apa yang terjadi pada contoh di atas:

  1. Kita buat sebuah React ref dengan memanggil React.createRef dan masukan kedalam variabel ref.
  2. Kita teruskan ref tersebut ke <FancyButton ref={ref}> dengan menulisnya sebagai atribut JSX.
  3. React meneruskan ref ke fungsi (props, ref) => ... di dalam forwardRef sebagai argumen kedua.
  4. Kita teruskan argumen ref tersebut ke <button ref={ref}> dengan menulisnya sebagai atribut JSX.
  5. Jika ref sudah terpasang, ref.current akan mengarah ke DOM node <button>.

Catatan

Argumen ref kedua hanya ada saat Anda mendefinisikan komponen dengan React.forwardRef. Regular function atau class components tidak menerima argumen ref tersebut, dan ref juga tidak teredia di props.

Ref forwarding tidak terbatas pada komponen DOM. Anda juga dapat meneruskan refs ke class component instances.

Catatan untuk pengelola pustaka komponen

Saat Anda mulai menggunakan forwardRef di pustaka komponen, Anda harus memperlakukannya sebagai sebuah breaking change dan merilis versi mayor baru pustaka Anda. Ini karena pustaka Anda kemungkinan besar memiliki perilaku yang sangat berbeda (seperti refs apa yang ditetapkan, dan tipe apa yang diekspor), dan ini bisa merusak aplikasi dan pustaka lain yang bergantung pada perilaku lama.

Menerapkan React.forwardRef secara kondisional jika ada juga tidak disarankan karena alasan yang sama: ini mengubah cara pustaka Anda berperilaku dan bisa merusak aplikasi pengguna Anda saat mereka meningkatkan versi React itu sendiri.

Mengoper refs dalam higher-order components

Teknik ini juga bisa menjadi sangat berguna untuk higher-order components (biasa disebut dengan HOCs). Mari kita mulai dengan contoh HOC yang mengeluarkan component props ke log konsol:

function logProps(WrappedComponent) {  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;    }
  }

  return LogProps;
}

HOC “logProps” meneruskan semua props ke komponen yang dibungkusnya, sehingga keluaran yang dirender akan sama. Misalnya, kita dapat menggunakan HOC ini untuk mengeluarkan semua props ke log konsol yang diteruskan ke “fancy button” komponen kita:

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// Daripada mengekspor FancyButton, kita mengekspor LogProps.
// LogProps tetap akan me-render FancyButton.
export default logProps(FancyButton);

Ada satu peringatan untuk contoh diatas: refs tidak akan diteruskan. Itu karena ref bukan sebuah prop. Seperti key, ini ditangani secara berbeda oleh React. Jika Anda menambahkan ref ke HOC, maka ref akan merujuk ke komponen container terluar, bukan komponen yang dibungkus.

Ini berarti bahwa ref yang dimaksudkan untuk komponen FancyButton kita sebenarnya akan dilampirkan ke LogProps komponen:

import FancyButton from './FancyButton';

const ref = React.createRef();
// Komponen FancyButton yang kita impor adalah HOC LogProps.
// Meskipun keluaran yang dirender akan sama,
// Ref kita akan menunjuk ke komponen LogProps daripada komponen FancyButton!
// Ini berarti kita tidak dapat memanggil ref, seperti ref.current.focus()
<FancyButton
  label="Klik saya!"
  handleClick={handleClick}
  ref={ref}/>;

Untungnya, kita dapat secara eksplisit meneruskan refs kedalam komponen FancyButton menggunakan React.forwardRef API. React.forwardRef menerima sebuah fungsi render yang menerima parameter props dan ref dan mengembalikan React node. Sebagai contoh:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;
      // Masukan prop kustom "forwardedRef" sebagai ref
      return <Component ref={forwardedRef} {...rest} />;    }
  }

  // Catat param kedua "ref" yang disediakan oleh React.forwardRef.
  // Kita dapat meneruskannya ke LogProps sebagai regular prop, misalnya "forwardedRef"
  // Dan kemudian dapat dilampirkan ke Komponen.
  return React.forwardRef((props, ref) => {    return <LogProps {...props} forwardedRef={ref} />;  });}

Menampilkan nama custom di DevTools

React.forwardRef menerima fungsi render. React DevTools menggunakan fungsi ini untuk menentukan apa yang akan ditampilkan untuk komponen ref forwarding.

Sebagai contoh, komponen berikut akan muncul sebagai ”ForwardRef” di DevTools:

const WrappedComponent = React.forwardRef((props, ref) => {
  return <LogProps {...props} forwardedRef={ref} />;
});

Jika Anda menamai fungsi render, DevTools juga akan menyertakan namanya (misalnya ”ForwardRef(myFunction)”):

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

Anda bahkan dapat menyetel properti displayName pada fungsi untuk menyertakan komponen yang Anda bungkus:

function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  // Beri komponen ini nama tampilan yang lebih berguna di DevTools.
  // misalnya, "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;  forwardRef.displayName = `logProps(${name})`;
  return React.forwardRef(forwardRef);
}

Is this page useful?Edit halaman ini