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.

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

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</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.

Forwarding refs dalam higher-order components

This technique can also be particularly useful with higher-order components (also known as HOCs). Let’s start with an example HOC that logs component props to the console:

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

The “logProps” HOC passes all props through to the component it wraps, so the rendered output will be the same. For example, we can use this HOC to log all props that get passed to our “fancy button” component:

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

  // ...
}

// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
export default logProps(FancyButton);

There is one caveat to the above example: refs will not get passed through. That’s because ref is not a prop. Like key, it’s handled differently by React. If you add a ref to a HOC, the ref will refer to the outermost container component, not the wrapped component.

This means that refs intended for our FancyButton component will actually be attached to the LogProps component:

import FancyButton from './FancyButton';

const ref = React.createRef();
// The FancyButton component we imported is the LogProps HOC.
// Even though the rendered output will be the same,
// Our ref will point to LogProps instead of the inner FancyButton component!
// This means we can't call e.g. ref.current.focus()
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}/>;

Fortunately, we can explicitly forward refs to the inner FancyButton component using the React.forwardRef API. React.forwardRef accepts a render function that receives props and ref parameters and returns a React node. For example:

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;
      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  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} />;
  }

  // Give this component a more helpful display name in DevTools.
  // e.g. "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;  forwardRef.displayName = `logProps(${name})`;
  return React.forwardRef(forwardRef);
}

Is this page useful?Edit halaman ini