diff --git a/packages/vue/src/composables/useRenderCount/index.test.ts b/packages/vue/src/composables/useRenderCount/index.test.ts new file mode 100644 index 0000000..2af0860 --- /dev/null +++ b/packages/vue/src/composables/useRenderCount/index.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from 'vitest'; +import { defineComponent, nextTick, ref } from 'vue'; +import { mount } from '@vue/test-utils' +import { useRenderCount } from '.'; + +const ComponentStub = defineComponent({ + setup() { + const count = useRenderCount(); + const visibleCount = ref(0); + const hiddenCount = ref(0); + + return { count, visibleCount, hiddenCount }; + }, + template: `
{{ visibleCount }}
`, +}); + +describe('useRenderCount', () => { + it('return the number of times the component has been rendered', async () => { + const component = mount(ComponentStub); + + // Initial render + expect(component.vm.count).toBe(0); + + component.vm.hiddenCount = 1; + await nextTick(); + + // Will not trigger a render + expect(component.vm.count).toBe(0); + expect(component.text()).toBe('0'); + + component.vm.visibleCount++; + await nextTick(); + + // Will trigger a render + expect(component.vm.count).toBe(1); + expect(component.text()).toBe('1'); + + component.vm.visibleCount++; + component.vm.visibleCount++; + await nextTick(); + + // Will trigger a single render for both updates + expect(component.vm.count).toBe(2); + expect(component.text()).toBe('3'); + }); + + it('can be used with a specific component instance', async () => { + const component = mount(ComponentStub); + const instance = component.vm.$; + + const count = useRenderCount(instance); + + // Initial render + expect(count.value).toBe(0); + + component.vm.hiddenCount = 1; + await nextTick(); + + // Will not trigger a render + expect(count.value).toBe(0); + + component.vm.visibleCount++; + await nextTick(); + + // Will trigger a render + expect(count.value).toBe(1); + + component.vm.visibleCount++; + component.vm.visibleCount++; + await nextTick(); + + // Will trigger a single render for both updates + expect(count.value).toBe(2); + }); +}); \ No newline at end of file diff --git a/packages/vue/src/composables/useRenderCount/index.ts b/packages/vue/src/composables/useRenderCount/index.ts new file mode 100644 index 0000000..0924dd9 --- /dev/null +++ b/packages/vue/src/composables/useRenderCount/index.ts @@ -0,0 +1,25 @@ +import { onUpdated, readonly, type ComponentInternalInstance } from 'vue'; +import { useCounter } from '../useCounter'; +import { getLifeCycleTarger } from '../../utils'; + +/** + * @name useRenderCount + * @category Components + * @description Returns the number of times the component has been rendered into the DOM + * + * @param {ComponentInternalInstance} [instance] The component instance to track the render count for + * @returns {Readonly>} The number of times the component has been rendered + * + * @example + * const count = useRenderCount(); + * + * @example + * const count = useRenderCount(getCurrentInstance()); + */ +export function useRenderCount(instance?: ComponentInternalInstance) { + const { count, increment } = useCounter(0); + + onUpdated(increment, getLifeCycleTarger(instance)); + + return readonly(count); +} \ No newline at end of file diff --git a/packages/vue/src/utils/components.ts b/packages/vue/src/utils/components.ts new file mode 100644 index 0000000..62eed3e --- /dev/null +++ b/packages/vue/src/utils/components.ts @@ -0,0 +1,5 @@ +import { getCurrentInstance, type ComponentInternalInstance } from 'vue'; + +export function getLifeCycleTarger(target?: ComponentInternalInstance) { + return target || getCurrentInstance(); +} \ No newline at end of file diff --git a/packages/vue/src/utils/index.ts b/packages/vue/src/utils/index.ts new file mode 100644 index 0000000..099b463 --- /dev/null +++ b/packages/vue/src/utils/index.ts @@ -0,0 +1 @@ +export * from './components'; \ No newline at end of file