Skip to content

Hooks integration #12

Closed
Closed
@yyx990803

Description

@yyx990803

Rationale

https://twitter.com/youyuxi/status/1056673771376050176

Hooks provides the ability to:

  • encapsulate arbitrarily complex logic in plain functions
  • does not pollute component namespace (explicit injection)
  • does not result in additional component instances like HOCs / scoped-slot components
  • superior composability, e.g. passing the state from one hook to another effect hook. This is possible by referencing fields injected by other mixins in a mixin, but that is super flaky and hooks composition is way cleaner.
  • compresses extremely well

However, it is quite different from the intuitions of idiomatic JS, and has a number of issues that can be confusing to beginners. This is why we should integrate it in a way that complements Vue's existing API, and primarily use it as a composition mechanism (replacement of mixins, HOCs and scoped-slot components).

Proposed usage

Directly usable inside class render functions (can be mixed with normal class usage):

class Counter extends Component {
  foo = 'hello'
  render() {
    const [count, setCount] = useState(0)
    return h(
      'div',
      {
        onClick: () => {
          setCount(count + 1)
        }
      },
      this.foo + ' ' + count
    )
  }
}

For template usage:

class Counter extends Component {
  static template = `
    <div @click="setCount(count + 1)">
      {{ count }}
    </div>
  `

  hooks() {
    const [count, setCount] = useState(0)
    // fields returned here will become available in templates
    return {
      count,
      setCount
    }
  }
}

In SFC w/ object syntax:

<template>
  <div @click="setCount(count + 1)">
    {{ count }}
  </div>
</template>

<script>
import { useState } from 'vue'

export default {
  hooks() {
    const [count, setCount] = useState(0)
    return {
      count,
      setCount
    }
  }
}
</script>

Note: counter is a super contrived example mainly to illustrate how the API works. A more practical example would be this useAPI custom hook, which is similar to libs like vue-promised.

Implementation Notes

Proposed usage for useState and useEffect are already implemented.

Update: Mapping w/ Vue's existing API

To ease the learning curve for Vue users, we can implement hooks that mimic Vue's current API:

export default {
  render() {
    const data = useData({
      count: 0
    })

    useWatch(() => data.count, (val, prevVal) => {
      console.log(`count is: ${val}`)
    })

    const double = useComputed(() => data.count * 2)

    useMounted(() => {
      console.log('mounted!')
    })
    useUnmounted(() => {
      console.log('unmounted!')
    })
    useUpdated(() => {
      console.log('updated!')
    })

    return [
      h('div', `count is ${data.count}`),
      h('div', `double count is ${double}`),
      h('button', { onClick: () => {
        // still got that direct mutation!
        data.count++
      }}, 'count++')
    ]
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions