DEV Community

rxliuli
rxliuli

Posted on

Using vue hooks in react

Scenes

When you see the title, you might think: Why do you do this?
React hooks are a useful invention that reorganizes the pattern of writing and thinking about code, using smaller abstractions to separate state and functions by function, rather than lumping them into a single state or lifecycle. But react hooks have the concept of state dependence, and what's worse: it relies on manual management -- although react officially provides eslint rules, if you have used it, you will find that it has a high false positive rate -- especially in in complex components.
So, does this mean there is no solution? No, the author of vue3 talked about the improvement of vue3 hooks compared to react hooks in a speech, one of which is that there is no need to manually manage dependencies, you can watch dotJS 2019 - Evan You - State of Components. But react is better ecologically (including various libraries and IDE development experience), so I try to implement automatic dependency management in react in some way, and implement some primitive hooks that behave like vue3.

The main sources of inspiration for this are solid.js and react-easy-state.

think

List of common primitive hooks for vue3

  • ref
  • reactive
  • computed
  • watchEffect
  • watch

When it comes to mutable state in react, the easiest thing to think of is mobx (as opposed to immutable redux), so the following will implement the above based on it hooks function.

accomplish

import { observer } from 'mobx-react'
import { action, autorun, computed, observable, reaction } from 'mobx'
import { useCallback, useEffect, useMemo, useState } from 'react'

export function useVReactive<T extends object>(value: T): T {
  const[state] = useState(() => observable(value))
  return state
}

export function useVRef<T>(value: T): { value: T } {
  return useVReactive({ value })
}

export function useVComputed<T>(fn: () => T): { value: T } {
  const computedValue = useMemo(() => computed(fn), [])
  return {
    get value() {
      return computedValue.get()
    },
  }
}

export function useVFn<T extends (...args: any[]) => any>(fn: T): T {
  return useCallback(action(fn), [])
}

export function useVWatch(deps: () => any, fn: () => void): void {
  useEffect(() => reaction(deps, fn), [])
}

export function useVWatchEffect(fn: () => void): void {
  useEffect(() => autorun(fn), [])
}

const contextMap = observable(new Map<ContextKey<any>, any>())
// eslint-disable-next-line
export interface ContextKey<T> extends Symbol {}
export function useVProvide<T>(key: ContextKey<T>, value: T): void {
  useState(action(() => contextMap.set(key, value)))
  useEffect(
    action(() => {
      contextMap.set(key, value)
      return action(() => {
        contextMap.delete(key)
      })
    }),
    [],
  )
}
export function useVInject<T>(key: ContextKey<T>): T | undefined {
  const value = useMemo(() => computed(() => contextMap.get(key)), [])
  const state = useVRef(value.get())
  useVWatchEffect(() => (state.value = value.get()))
  return state.value
}

export const defineComponent = observer
Enter fullscreen mode Exit fullscreen mode

We have published it to npm @liuli-util/mobx-vue3-hooks

use

It feels similar to vue3 hooks to use, just declare a state, then modify it directly, and everything will respond automatically - no need to manually manage dependencies and understand the mental model of hooks.

import {
  defineComponent,
  useVRef,
  useVFn,
  useVComputed,
} from '@liuli-util/mobx-vue3-hooks'

const HelloWorld = defineComponent(() => {
  const state = useVRef(0)
  const computedValue = useVComputed(() => state.value * 2)

  const onInc = useVFn(() => {
    state.value++
  })
  return (
    <div>
      <button onClick={onInc}>Add</button>
      <div>{computedValue.value}</div>
    </div>
  )
})
Enter fullscreen mode Exit fullscreen mode

Due to some reasons, we have not fully realized the effect of vue3 hooks, such as

  • You need to use useVFn to wrap the function of the operation state, but in vue3, you only need to declare a normal function in the setup function.
  • useWatch uses computed functions, while vue3 uses dependent state arrays
  • Must use defineComponent to wrap the component, in vue3 it is just a code hint

Summarize

The react ecology does have all kinds of things, and the ecology is super rich, but some details are relatively rough, especially when the official let it go. My generation used to have this question: Why is no one feeling weird about what is happening now? For example, react-router v4=>v6 blind update, material-ui/fluentui form and table components are almost unavailable compared to antd, redux complexity is still necessary as the default state manager, react hooks dependency management depends on Labor is very annoying, why do dozens of css in js schemes not be managed by the official government and so on.

Top comments (0)