2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-10-22

経緯

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 を再現する

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

既存のライブラリ

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

  • github:yyx990803/vue-hooks

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

  • github:TotooriaHyperion/vue-hooks

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

  • github:zephraph/vue-context-api

    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)
			}
		},
	})
})

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

2
0
1

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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?