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

Vue と typeless を組み合わせるために Vue 用に React Hooks を作る

経緯

React 用に作られた状態管理ライブラリ typeless が良さげだったのですが
最近使っているのが Vue なので Vue と typeless を組み合わせたいなと思いました。

完成品

github:ytoune/practice-20191019-typeless-with-vue-hooks
動作例

hooks を使う

src/comps/CountUp.vue
<template>
    <div>
        <p>count: {{ count }}</p>
        <p><button @click="countUp">+1</button></p>
    </div>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { useActions } from 'typeless'
import { useModule, CounterActions, getCounterState } from '~/store'

@Component({
    hooks() {
        useModule()
        const acts = useActions(CounterActions)
        const state = getCounterState.useState()
        const count = state.count
        const countUp = acts.startCount
        return { count, countUp }
    },
})
export default class CountUp extends Vue {
    count!: number
    countUp!: Function
}
</script>

問題点

typeless は React の hooks に依存しています。
具体的にはソースコードに import * as React from 'react' と書かれています。

今回は Vue 用に React の API を再現します。

ディレクトリ構造

- workspace
  |- src
  |  |- index.ts
  |  |- App.vue
     # その他省略
  |- package.json
  |- tsconfig.json
  # その他省略

パスの設定

TypeScript と Parcel のパスの設定を行います。
(今回は Parcel を使っています)

tsconfig.json
{
  "compilerOptions": {
    /* 略 */
    "paths": {
      "~/*": ["*"],
      "react": ["src/react/core.ts"],
      "react-dom": ["src/react/dom.ts"]
    },
    /* 略 */
  },
  /* 略 */
}
package.json
{
  /* 略 */
  "alias": {
    "react": "./src/react/core.ts",
    "react-dom": "./src/react/dom.ts"
  }
  /* 略 */
}

Vue 用に React の API を再現する

実装優先でパフォーマンスなどを無視しているところが多いです。

既存のライブラリ

できる限り既存の遺産を再利用したいですね。

Vue フレームワークの創始者である Evan You さんが似たようものを作り始めているみたいです。
useState などの一部の hooks が実装されています。

上記の fork です。
useMemo などが追加されています。

createContextProvider などの React の ContextAPI の Vue 向け実装です。
ただし useContext がないです。

不足している実装を埋める

useReducer

src/react/hooks.ts
export const useReducer = (reducer: any, initialState: any) => {
    const [s, update] = useState(initialState)
    const dispatch = (action: any) => {
        update(reducer(s, action))
    }
    return [s, dispatch]
}

useLayoutEffect

とりあえず useEffect を複製してます。

src/react/hooks.ts
export const useLayoutEffect = useEffect

useContext

vue-context-api では、値を context.value に代入しています。
これを useContext で取り出します。

+const getcontext = '#context'
export const createContext = (defaultValue: any) => {
    const context = {}
    return {
+       [getcontext]: context,
        Provider: Provider(context, defaultValue),
        // Consumer: Consumer(context)
        Consumer: null,
    }
}
src/react/hooks.ts
export function useContext(context: any) {
    return context[getcontext].value
}

unstable_batchedUpdates

すべてのインスタンスを $forceUpdate してます。
ゴリ押しです。

src/react/dom.ts
export const unstable_batchedUpdates = (fn: () => void) => {
    fn()
    for (const ins of list) ins.$forceUpdate()
}
export const batchedUpdates = unstable_batchedUpdates

const list: any[] = []

Vue.use(function(Vue: any) {
    Vue.mixin({
        beforeCreate() {
            list.push(this)
        },
        beforeDestroy() {
            const i = list.indexOf(this)
            if (~i) {
                list.splice(i, 1)
            }
        },
    })
})

とりあえず上記を実装でも動くみたいです。

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
ユーザーは見つかりませんでした