Help us understand the problem. What is going on with this article?

続・JSフレームワークの末端がWebComponentsになるのか、なれるのか、検証してみた

More than 1 year has passed since last update.

https://qiita.com/mizchi/items/053f5b42a6d0902e9412

やりたかったこと: Reactから子に関数参照を渡すのを綺麗に実装したい

前回からの変化

  • lit-extended を使えば、テンプレートにコールバックを渡せることを知った
  • 子に対して関数参照を渡すのではなく、 CustomEvent を生成するようにして dispatchEvent するようにした
  • 一旦変換用のイベント定義辞書を渡すようにした

WebComponent定義

/* @flow */
import { html, render } from 'lit-html/lib/lit-extended'

type Props = {
  onClick?: Function,
  text: string
}

const template = (props: Props) => {
  return html`
    <button on-click=${props.onClick}>${props.text}</button>
  `
}

export default class MyButton extends HTMLElement {
  static get observedAttributes(): string[] {
    return ['text']
  }

  connectedCallback() {
    this.render()
  }

  attributeChangedCallback(_attrName: string, _old: any, _new: any) {
    this.render()
  }

  render() {
    const text = this.getAttribute('text') || 'button'
    const onClick = () =>
      this.dispatchEvent(
        new CustomEvent('my-button-clicked', {
          detail: {
            text
          }
        })
      )
    render(template({ text, onClick }), this)
  }
}

window.customElements.define('my-button', MyButton)

静的な値だけを一旦渡すようにした。

React側

reactify という関数で、特定のエレメントをReactで表示する際にどう値を渡すか定義する

/* @flow */
import ReactDOM from 'react-dom'
import * as React from 'react'
import '~/elements/MyButton'

const reactify = (name: string, opts = {}) =>
  class extends React.PureComponent {
    constructor() {
      super()
      this._listeners = []
    }
    componentDidMount() {
      const el = ReactDOM.findDOMNode(this)
      for (const eventName of Object.keys(opts.eventMap || {})) {
        const translated = opts.eventMap[eventName]
        const callback = this.props[translated]
        console.log('register', eventName, translated)
        el.addEventListener(eventName, callback)
        this._listeners.push({ eventName, callback })
      }
    }
    componentWillUnmount() {
      this._listeners.forEach(listener => {
        el.removeEventListener(listener.eventName, listener.callback)
      })
      this._listeners = []
    }
    render() {
      return React.createElement(name, this.props)
    }
  }

const MyButtonReact = reactify('my-button', {
  eventMap: {
    'my-button-clicked': 'onMyButtonClicked'
  }
})

export default function Home() {
  return (
    <div>
      <MyButtonReact
        text="my button on react"
        onMyButtonClicked={_ev => console.log('clicked!')}
      />
      {/* <my-button text="my-button" /> */}
    </div>
  )
}

「このイベントが来たらこのコールバックに渡す」という中間層を用意したことで、とりあえず動くようになった。

残ってる点

  • 本当は define した名前ではなく オブジェクト参照を使いたいが React.createElement が対応していないので、一旦こうなった。
  • やっぱ eventMap ダサい…
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした