diff --git a/packages/runtime-core/__tests__/hooks.spec.ts b/packages/runtime-core/__tests__/hooks.spec.ts new file mode 100644 index 00000000..51ccd39f --- /dev/null +++ b/packages/runtime-core/__tests__/hooks.spec.ts @@ -0,0 +1,60 @@ +import { withHooks, useState, h, nextTick, useEffect } from '../src' +import { renderIntsance, serialize, triggerEvent } from '@vue/runtime-test' + +describe('hooks', () => { + it('useState', async () => { + const Counter = withHooks(() => { + const [count, setCount] = useState(0) + return h( + 'div', + { + onClick: () => { + setCount(count + 1) + } + }, + count + ) + }) + + const counter = renderIntsance(Counter) + expect(serialize(counter.$el)).toBe(`
0
`) + + triggerEvent(counter.$el, 'click') + await nextTick() + expect(serialize(counter.$el)).toBe(`
1
`) + }) + + it('useEffect', async () => { + let effect = -1 + + const Counter = withHooks(() => { + const [count, setCount] = useState(0) + useEffect(() => { + effect = count + }) + return h( + 'div', + { + onClick: () => { + setCount(count + 1) + } + }, + count + ) + }) + + const counter = renderIntsance(Counter) + expect(effect).toBe(0) + triggerEvent(counter.$el, 'click') + await nextTick() + expect(effect).toBe(1) + }) + + it('useEffect with empty keys', async () => { + // TODO + }) + + it('useEffect with keys', async () => { + // TODO + }) +}) diff --git a/packages/runtime-core/src/optional/hooks.ts b/packages/runtime-core/src/optional/hooks.ts index 9303613d..c195c2c7 100644 --- a/packages/runtime-core/src/optional/hooks.ts +++ b/packages/runtime-core/src/optional/hooks.ts @@ -1,4 +1,4 @@ -import { ComponentInstance, FunctionalComponent } from '../component' +import { ComponentInstance, FunctionalComponent, Component } from '../component' import { mergeLifecycleHooks, Data } from '../componentOptions' import { VNode, Slots } from '../vdom' import { observable } from '@vue/observer' @@ -36,7 +36,7 @@ export function unsetCurrentInstance() { currentInstance = null } -export function useState(initial: any) { +export function useState(initial: T): [T, (newValue: T) => void] { if (!currentInstance) { throw new Error( `useState must be called in a function passed to withHooks.` @@ -107,20 +107,20 @@ function injectEffect( : effect } -export function withHooks(render: T): T { - return { - displayName: render.name, +export function withHooks(render: FunctionalComponent): new () => Component { + return class ComponentWithHooks extends Component { + static displayName = render.name created() { - hooksState.set(this._self, { + hooksState.set((this as any)._self, { state: observable({}), effects: [] }) - }, + } render(props: Data, slots: Slots, attrs: Data, parentVNode: VNode) { - setCurrentInstance(this._self) + setCurrentInstance((this as any)._self) const ret = render(props, slots, attrs, parentVNode) unsetCurrentInstance() return ret } - } as any + } }