揮発性コンポーネントにuseRefをつかう


ずっとVueやってたんですが、最近React Hooksの登場でまたReactをかじりだしました。

Vueだと揮発性コンポーネント(ポップアップやスナックバーなどの出ては消えていくもの)をグローバルメソッドとして操るには、下記のスライドのようなことをする必要があります。

Reactだとどうなるのか。useRefを使うといいです。

(っていうかuseRef使い勝手良すぎ…?)

useRefとは

以下のようにして作ったrefContainercurrentプロパティには、initialValueで指定した、プレーンオブジェクトが入っていて、そのコンポーネントが存在している限りず~~っと同じ参照を持っていて、currentプロパティの値も変更可能です。

つまり、どこからでも参照可能で変更可能な値を作れます。しかも、変更しても再描画はしません。

const refContainer = useRef(initialValue);

コンポーネント内で好きなcurrentを入れてあげれば、それを通して関数を呼び出したりできちゃうんです。

実装

App.jsは最上層かページコンポーネントかなんかだと思ってください。

useRef(null)としていますが、それをPopupコンポーネントのpopup属性に渡しています。

そうすると、popup.current.open()popup.current.close()というようにできていることがわかります。

app.js
  import React, { useRef } from "react"

  import Layout from "../components/layout"
  import Popup from "../components/popup"

  const App = () => {
    const popup = useRef(null)

    return (
      <Layout>
        <button onClick={() => popup.current.open()}>ポップアップを開く</button>
        <button onClick={() => popup.current.close()}>ポップアップを閉じる</button>

        <Popup popup={popup}>
          Hello useRef Popup!!
        </Popup>
      </Layout>
    )
  }

  export default App

Popupコンポーネントを見ていきましょう。

受け取ったpopup属性のcurrentに値を入れてあげます。

下記の2つのメソッドを作ってあげます。それぞれ実行後のCSSクラスが切り替わり、Transitionするようにしました。

  • open()メソッド ⇒ .popup.popup__visible
  • close()メソッド ⇒ .popup
popup.js
import React, { useState } from "react"

const Popup = ({ name, popup, children }) => {
  const [visible, setVisible] = useState(false)

  popup.current = {
    open() {
      setVisible(true)
    },
    close() {
      setVisible(false)
    },
  }

  return (
    <div
      className={`popup ${visible ? "popup__visible" : ""}`}
      name={visible ? name : ""}
    >
      {children}
    </div>
  )
}

export default Popup

おまけのCSS

.popup {
  opacity: 0;
  transform: translateY(-300px);
  transition: all 1s ease 0s;
}

.popup__visible {
  opacity: 1;
  transform: translateY(0px);
}

opecity変わるだけなので、ポップアップ感ないですが、まあやりたいことはできたと思います。

このようにuseRefはプレーンオブジェクトを持つコンテナを作ってくれるだけなので、上書きするもよし、どんなオブジェクトだろうがなにを入れようが問題無しです。

なので、メソッド形式でコンポーネント内部の値を書き換えたいという要求も簡単に叶えることができてしまうのでした。

hook便利すぎですね。以上でした~。