feat(vue): add composition API ionic lifecycle hooks (#22970)

resolves #22769
This commit is contained in:
Liam DeBeasi
2021-03-01 10:35:25 -05:00
committed by GitHub
parent 9486e51f6d
commit dd1c8dbf3b
6 changed files with 134 additions and 9 deletions

View File

@ -1,5 +1,12 @@
import { BackButtonEvent } from '@ionic/core';
import { inject, ref, Ref } from 'vue';
import {
inject,
ref,
Ref,
ComponentInternalInstance,
getCurrentInstance
} from 'vue';
import { LifecycleHooks } from './utils';
type Handler = (processNextHandler: () => void) => Promise<any> | void | null;
@ -62,3 +69,38 @@ export const useKeyboard = (): IonKeyboardRef => {
unregister
}
}
/**
* Creates an returns a function that
* can be used to provide a lifecycle hook.
*/
const injectHook = (lifecycleType: LifecycleHooks, hook: Function, component: ComponentInternalInstance | null): Function | undefined => {
if (component) {
// Add to public instance so it is accessible to IonRouterOutlet
const target = component as any;
const hooks = target.proxy[lifecycleType] || (target.proxy[lifecycleType] = []);
const wrappedHook = (...args: unknown[]) => {
if (target.isUnmounted) {
return;
}
return args ? hook(...args) : hook();
};
hooks.push(wrappedHook);
return wrappedHook;
} else {
console.warn('[@ionic/vue]: Ionic Lifecycle Hooks can only be used during execution of setup().');
}
}
const createHook = <T extends Function = () => any>(lifecycle: LifecycleHooks) => {
return (hook: T, target: ComponentInternalInstance | null = getCurrentInstance()) => injectHook(lifecycle, hook, target);
}
export const onIonViewWillEnter = createHook(LifecycleHooks.WillEnter);
export const onIonViewDidEnter = createHook(LifecycleHooks.DidEnter);
export const onIonViewWillLeave = createHook(LifecycleHooks.WillLeave);
export const onIonViewDidLeave = createHook(LifecycleHooks.DidLeave);

View File

@ -16,7 +16,17 @@ export { IonApp } from './components/IonApp';
export * from './components/Overlays';
export { IonKeyboardRef, IonRouter, useBackButton, useIonRouter, useKeyboard } from './hooks';
export {
IonKeyboardRef,
IonRouter,
useBackButton,
useIonRouter,
useKeyboard,
onIonViewWillEnter,
onIonViewDidEnter,
onIonViewWillLeave,
onIonViewDidLeave
} from './hooks';
export {
modalController,

View File

@ -3,6 +3,19 @@ import { Config as CoreConfig, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYC
type LIFECYCLE_EVENTS = typeof LIFECYCLE_WILL_ENTER | typeof LIFECYCLE_DID_ENTER | typeof LIFECYCLE_WILL_LEAVE | typeof LIFECYCLE_DID_LEAVE;
export enum LifecycleHooks {
WillEnter = 'onIonViewWillEnter',
DidEnter = 'onIonViewDidEnter',
WillLeave = 'onIonViewWillLeave',
DidLeave = 'onIonViewDidLeave'
}
const hookNames = {
[LIFECYCLE_WILL_ENTER]: LifecycleHooks.WillEnter,
[LIFECYCLE_DID_ENTER]: LifecycleHooks.DidEnter,
[LIFECYCLE_WILL_LEAVE]: LifecycleHooks.WillLeave,
[LIFECYCLE_DID_LEAVE]: LifecycleHooks.DidLeave
}
const ids: { [k: string]: number } = { main: 0 };
export const generateId = (type = 'main') => {
@ -21,6 +34,18 @@ export const fireLifecycle = (vueComponent: any, vueInstance: Ref<ComponentPubli
if (instance?.[lifecycle]) {
instance[lifecycle]();
}
/**
* Fire any Composition API
* Ionic Lifecycle hooks
*/
if (instance) {
const hook = hookNames[lifecycle];
const hooks = instance[hook];
if (hooks) {
hooks.forEach((hook: Function) => hook());
}
}
}
export const getConfig = (): CoreConfig | null => {

View File

@ -22,6 +22,11 @@
ionViewWillLeave: <div id="willLeave">{{ willLeave }}</div><br />
ionViewDidLeave: <div id="didLeave">{{ didLeave }}</div><br />
onIonViewWillEnter: <div id="onWillEnter">{{ onWillEnter }}</div><br />
onIonViewDidEnter: <div id="onDidEnter">{{ onDidEnter }}</div><br />
onIonViewWillLeave: <div id="onWillLeave">{{ onWillLeave }}</div><br />
onIonViewDidLeave: <div id="onDidLeave">{{ onDidLeave }}</div><br />
<ion-button router-link="/navigation" id="lifecycle-navigation">Go to another page</ion-button>
</div>
</ion-content>
@ -37,9 +42,13 @@ import {
IonHeader,
IonPage,
IonTitle,
IonToolbar
IonToolbar,
onIonViewWillEnter,
onIonViewDidEnter,
onIonViewWillLeave,
onIonViewDidLeave
} from '@ionic/vue';
import { defineComponent } from 'vue';
import { defineComponent, ref } from 'vue';
export default defineComponent({
components: {
IonButton,
@ -65,6 +74,24 @@ export default defineComponent({
this.didLeave++;
}
},
setup() {
const onWillEnter = ref(0);
const onDidEnter = ref(0);
const onWillLeave = ref(0);
const onDidLeave = ref(0);
onIonViewWillEnter(() => onWillEnter.value += 1);
onIonViewDidEnter(() => onDidEnter.value += 1);
onIonViewWillLeave(() => onWillLeave.value += 1);
onIonViewDidLeave(() => onDidLeave.value += 1);
return {
onWillEnter,
onDidEnter,
onWillLeave,
onDidLeave
}
},
data() {
return {
willEnter: 0,

View File

@ -7,7 +7,11 @@ describe('Lifecycle', () => {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0
ionViewDidLeave: 0,
onIonViewWillEnter: 1,
onIonViewDidEnter: 1,
onIonViewWillLeave: 0,
onIonViewDidLeave: 0
});
cy.get('#lifecycle-navigation').click();
@ -16,7 +20,11 @@ describe('Lifecycle', () => {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 1,
ionViewDidLeave: 1
ionViewDidLeave: 1,
onIonViewWillEnter: 1,
onIonViewDidEnter: 1,
onIonViewWillLeave: 1,
onIonViewDidLeave: 1
});
cy.ionBackClick('navigation');
@ -25,7 +33,11 @@ describe('Lifecycle', () => {
ionViewWillEnter: 2,
ionViewDidEnter: 2,
ionViewWillLeave: 1,
ionViewDidLeave: 1
ionViewDidLeave: 1,
onIonViewWillEnter: 2,
onIonViewDidEnter: 2,
onIonViewWillLeave: 1,
onIonViewDidLeave: 1
});
});
@ -36,7 +48,11 @@ describe('Lifecycle', () => {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0
ionViewDidLeave: 0,
onIonViewWillEnter: 1,
onIonViewDidEnter: 1,
onIonViewWillLeave: 0,
onIonViewDidLeave: 0
});
});
})
@ -46,4 +62,9 @@ const testLifecycle = (selector, expected = {}) => {
cy.get(`[data-pageid=${selector}] #didEnter`).should('have.text', expected.ionViewDidEnter);
cy.get(`[data-pageid=${selector}] #willLeave`).should('have.text', expected.ionViewWillLeave);
cy.get(`[data-pageid=${selector}] #didLeave`).should('have.text', expected.ionViewDidLeave);
cy.get(`[data-pageid=${selector}] #onWillEnter`).should('have.text', expected.onIonViewWillEnter);
cy.get(`[data-pageid=${selector}] #onDidEnter`).should('have.text', expected.onIonViewDidEnter);
cy.get(`[data-pageid=${selector}] #onWillLeave`).should('have.text', expected.onIonViewWillLeave);
cy.get(`[data-pageid=${selector}] #onDidLeave`).should('have.text', expected.onIonViewDidLeave);
}

View File

@ -10,7 +10,7 @@
"module": "es2015",
"moduleResolution": "node",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitReturns": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist-transpiled",