Description
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++')
]
}
}