1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HTML要素とJavaScriptの値を紐づけるには、WeakMapを使うといいらしい。

Posted at

たとえばReactで下記のような配列からHTMLを描画する場合。

const buttonList = [
  {
    key: 0,
    text: 'テキストA',
    order: 3,
  },
  {
    key: 1,
    text: 'テキストB',
    order: 2,
  },
  {
    key: 2,
    text: 'テキストC',
    order: 1,
  },
];

やりたいこと

このとき、描画した要素にクリックイベントを付与し、
クリックされたときに対象のオブジェクトに紐づく情報を参照したい場合がある。

function onClickButton(e) {
  // ここで e.target に紐づく key や order などの情報を利用したい
}

const Buttons = ()=> {
  return buttonList.map(button => <>
    <button onclick={onClickButton}>
      {button.text}
    </button>
  </>);
}

従来のやり方

自分がもともと知ってるやり方だと、
data-key属性やdata-object属性を付与して関連する値をHTML要素に持たせるという方法があった。

function onClickButton(e) {
  console.log(e.target.dataset.key);
  console.log(e.target.dataset.order);
}

const Buttons = ()=> {
  return buttonList.map(button => <>
    <button
      onclick={onClickButton}
      data-key={button.key}
      data-order={button.order}
      >
      {button.text}
    </button>
  </>);
}

WeakMapを使ったやり方

こういうとき、refを利用してWeakMapに値の紐づけをしておくと、
あとから要素に紐づく値の情報を参照することができるらしい。

const buttonDataMap = new WeakMap();

function onClickButton(e) {
  const buttonData = buttonDataMap.get(e.currentTarget);
  console.log(buttonData.key);
  console.log(buttonData.order);
}

const Buttons = ()=> {
  return buttonList.map(button => {
    const ref = (el) => {
      if (el) buttonDataMap.set(el, button);
    };
    
    return <>
      <button
        onclick={onClickButton}
        ref={ref}
        >
        {button.text}
      </button>
    </>
  };
}

e.targetで取得できるのはイベントを拾った要素。
  e.currentTargetは、イベントを持たせた要素になるので、
  今回の処理はe.currentTargetを利用したほうが確実とのこと。

WeakMapを使うメリット

やりたいことが単純な場合は、従来のやり方でも問題なさそうだが、
いちいち文字列から数値へ型変換するのが面倒だったり、
関連するオブジェクトへの参照が失われる という問題がある。

WeakMapだと直接関連するオブジェクトの参照を取得することができるので、
いろいろ柔軟にやりたいときに、便利そうだ。

通常のMapでも同様の紐づけはできるらしいが、
WeakMapを使うと、refがnullになった場合に自動的に関連する値の情報も破棄されるとのこと。
つまり、コンポーネントを破棄したときの後処理を考える必要がなくて楽なんだとか。

ちなみに

今回の例のような処理を実装したい場合、
おそらく小さなコンポーネントをもう1つ作成して
そのコンポーネントにpropsとして関連づく値を渡したほうが
後々のコードがシンプルに保てそうである。

function onClickButton(buttonData) {
  console.log(buttonData.key);
  console.log(buttonData.order);
}

const Button = (buttonData)=> <>
  <button onclick={()=>{ onClickButton(buttonData) }}>
    {button.text}
  </button>
</>;

const Buttons = ()=> {
  return buttonList.map(buttonData => Button(buttonData));
}

ただ、なんとなくWeakMapが活躍する場面もありそうなので、備忘録としてここにメモしておく。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?