1. ホーム
  2. ジャバスクリプト

Reactのrefについて

2022-03-24 10:27:38

refの役割

reactのデータフローはトップダウンで、子コンポーネントの状態を変更する場合のみpropsを渡す必要があることが分かっていますが、特定のシナリオでは、子コンポーネントのインスタンスやdom要素を取得して、その要素の動作を独自に操作したいので、refを使って子コンポーネントやdom要素を取得する必要があるのです

シナリオ: アニメーション、またはある時点で入力ボックスにフォーカスを当てたい

ref.を使うときの3大原則

1. dom要素の上でref属性を使用することができます

2. クラスコンポーネントの上で ref 属性を使用することができます。

3. 関数コンポーネントの上でref属性を使うことはできません。関数コンポーネントはインスタンスを持っておらず、親コンポーネントは子コンポーネントのrefを取得することで、実際には子コンポーネントのインスタンスを取得しているからです(ただし、関数コンポーネントで子コンポーネントのインスタンスのrefを取得して変数に格納すれば、問題ありません)。

個人的には、ファンクションコンポーネントは、ステートやライフサイクルのメソッドが使えない、ref属性が使えないなど、まだまだ制約が多いと感じています(開発中に遭遇した問題、詳細は下記参照)

基本的な使い方

class App extends React.
  constructor() {
    super();
    this.inputRef = React.createRef();
  }
  handleClick = () => {
    this.inputRef.current.focus();
  };

  render() {
    return (
      
); } } Copy the code

この例では react16.3+バージョン (古いバージョンを使いたい場合は下記を参照)、3つのステップがあります。

1. createRef メソッド refオブジェクトを生成する

2.render は子要素または dom 要素の ref 属性を受け取る

3. this.inputRef.currentを使用して、子要素またはdom要素を取得します。

コールバックはrefを使用します。

一般的にreact16.2以下で使用されます。

  componentDidMount() {
    if (this.img && this.img.complete) { //this is where you get the img dom element
      this.onLoad()
    }
  }

  render() {
    const {className, src, alt, onClick, onRef, preview} = this.props

    return (
        {preview && !src ? (
          
) : ( { this.img = node if (onRef) { onRef() } }} src={src} alt={alt} onLoad={this.onLoad} onClick={onClick} /> )} ) } Copy code

ご覧の通り、コールバックを使って直接refを取得することも可能で、個人的にはこちらの方がシンプルだと感じていますが、作者がこの方法を捨てた理由はまだよく分かりません。その コンポーネントをまたいで渡すのが面倒だからだと思うのですが ~. また、コードを見ると、onRefメソッドはrefを直接上位コンポーネントに渡すことができるようなので、新しいバージョンのapiを使ってrefを上位コンポーネントに渡したい場合、その方法はどうすればいいのでしょうか?を参照してください。 forwardRef

フォワードレフ

const MyInput = React.forwardRef((props, ref) => (
    <div>
      I am Custom input component
      <input ref={ref}/>
    </div>
  ))

class App extends React.
  constructor() {
    super();
    this.inputRef = React.createRef();
  }
  handleClick = () => {
    this.inputRef.current.focus();
  };
  render() {
    return (
      <div className="App">
        <button onClick={this.handleClick}>click me Conduct focus</button>
        <MyInput ref={this.inputRef}/>
      </div>
    );
  }
}
Copy the code

上位のコンポーネントの中にあるdom要素や子コンポーネントのインスタンスを取得したい場合は、React.forwardRefを使ってコンポーネントをレイヤーで囲みます。実行順序は 上位コンポーネント createRef => アプリコンポーネントはMyInputにrefを渡す => MyInputはforwardRefメソッドでrefを受け取る => input入力ボックスはrefにノードを渡す(これはすでに上位コンポーネントのrefになっています)。

上位コンポーネントでのrefの使用

forwardRefを使用するシナリオはそれほど多くありませんが、時にはコンポーネントをまたいでrefを渡す必要がある場合があります。あるロジックを実装するために、実際にはコンポーネントをレイヤーにカプセル化した上位レベルのコンポーネントを使用することは知っていますが、上位レベルのコンポーネントでrefを取得したい場合、上位レベルのコンポーネントのrefを取得します、なぜでしょうか?例えば:最上位コンポーネントはApp、上位コンポーネントはMyHOC、子コンポーネントはMyInputと呼ばれ、このMyInputをAppで取得したいので、次のように使うのは間違いです。

次の例では、アプリは実際にはFooのrefを受け取りますが、Fooはrefを持ちません。

// MyInput.js
import MyHOC from '. /MyHOC'
const MyInput = () => (
    
I am Custom input component
) return MyHOC(MyInput) // MyHOC.js const MyHOC = (WrappedComponent) => { // do sth here... class Foo extends React. render() { return <WrappedComponent {. .this.props} />; } } return <Foo />; } export default MyHOC //App.js import MyInput from '. /MyInput'; const ref = React.createRef(); <MyInput ref={ref} // The ref here is actually Foo />; Copy the code

では、Hocではどのように使うのが正しいのでしょうか?

// MyInput.js
import MyHOC from '. /MyHOC'
const MyInput = () => (
    
I am Custom input component
) return MyHOC(MyInput) // MyHOC const MyHOC = (WrappedComponent) => { class Foo extends React. render() { const { forwardedRef, . .rest } = this.props; // At this point the ref is the WrappedComponent return <WrappedComponent ref={forwardedRef} {. .rest} />; } } // Wrap a layer with React.forwardRef, so that the ref of the higher-level component is named forwardedRef (an arbitrarily defined property name) and passed to the child component return React.forwardRef((props, ref) => { return <Foo {. .props} forwardedRef={ref} />; }); } export default MyHOC //App.js import MyInput from '. /MyInput'; const ref = React.createRef(); <MyInput ref = {ref} />; Copy code

上のコードにあるように、違いはただ一つ。 MyHOC コンポーネントは React.forwardRef を渡しているため、外側のコンポーネントが受け取る参照はFooではなく、本物のMyInputコンポーネントとなります。

問題点と解決策

  • 機能コンポーネントがref属性を使えない(開発中もこの問題に遭遇しました、ant designのgetFieldDecorator()(myFnComponent)を使うと、"Unable to mount ref's" というエラーが報告されました)
        <Form.Item
          label={field.label}
          key={
`key_${field.label}`}
        >
          {render
            ? render()
            : getFieldDecorator(field.fieldName, {
                rules: R.pathOr([], ['config', 'rules'], field),
                initialValue: data[field.field],
              })(
)} // The Item here is a functional component, so this will be wrong
         // })(Item(field))} This is ok, because Item will return a class component
        
// Error message: Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.

Copy the code

  • 1つのコンポーネントの中に複数のrefがある場合はどうでしょうか?その場合は、refを複数回作成します。
  • 注意:コンポーネントがマウントされる前にthis.myRef.currentを取得した場合、そのコンポーネントは null で返され、refは componentDidMount  または  componentDidUpdate を更新する前に

参考リンク

gist.github.com/gaearon/1a0...

reactjs.org/docs/refs-a...

ant.design/components/...

reactjs.org/docs/forwar...