mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 17:42:15 +08:00
fix(react): duplicate events being fired in ionic/react (#17321)
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import { Components } from '@ionic/core';
|
||||
import { createOverlayComponent } from './createOverlayComponent';
|
||||
import { Omit } from './types';
|
||||
import { Omit } from '../types';
|
||||
|
||||
export type ModalOptions = Omit<Components.IonModalAttributes, 'component' | 'componentProps'> & {
|
||||
children: React.ReactNode;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Components } from '@ionic/core';
|
||||
import { createOverlayComponent } from './createOverlayComponent';
|
||||
import { Omit } from './types';
|
||||
import { Omit } from '../types';
|
||||
|
||||
export type PopoverOptions = Omit<Components.IonPopoverAttributes, 'component' | 'componentProps'> & {
|
||||
children: React.ReactNode;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { attachEventProps } from './utils'
|
||||
import { ensureElementInBody, dashToPascalCase } from './utils';
|
||||
import { OverlayComponentElement, OverlayControllerComponentElement } from './types';
|
||||
import { OverlayComponentElement, OverlayControllerComponentElement } from '../types';
|
||||
|
||||
export function createControllerComponent<T extends object, E extends OverlayComponentElement, C extends OverlayControllerComponentElement<E>>(tagName: string, controllerTagName: string) {
|
||||
const displayName = dashToPascalCase(tagName);
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { attachEventProps } from './utils'
|
||||
import { ensureElementInBody, dashToPascalCase } from './utils';
|
||||
import { OverlayComponentElement, OverlayControllerComponentElement } from './types';
|
||||
import { OverlayComponentElement, OverlayControllerComponentElement } from '../types';
|
||||
|
||||
export function createOverlayComponent<T extends object, E extends OverlayComponentElement, C extends OverlayControllerComponentElement<E>>(tagName: string, controllerTagName: string) {
|
||||
const displayName = dashToPascalCase(tagName);
|
||||
|
@ -64,7 +64,11 @@ class IonTabBar extends Component<Props, State> {
|
||||
|
||||
|
||||
onTabButtonClick = (e: CustomEvent<{ href: string, selected: boolean, tab: string }>) => {
|
||||
this.props.history.push(e.detail.href);
|
||||
const targetUrl = (this.state.activeTab === e.detail.tab) ?
|
||||
this.state.tabs[e.detail.tab].originalHref :
|
||||
this.state.tabs[e.detail.tab].currentHref;
|
||||
|
||||
this.props.history.push(targetUrl);
|
||||
}
|
||||
|
||||
renderChild = (activeTab: string) => (child: React.ReactElement<Components.IonTabButtonAttributes & { onIonTabButtonClick: (e: CustomEvent) => void }>) => {
|
||||
|
@ -1,17 +1,32 @@
|
||||
/**
|
||||
* Checks if an event is supported in the current execution environment.
|
||||
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
||||
*/
|
||||
function isCoveredByReact(eventNameSuffix: string) {
|
||||
const eventName = 'on' + eventNameSuffix;
|
||||
let isSupported = eventName in document;
|
||||
|
||||
if (!isSupported) {
|
||||
const element = document.createElement('div');
|
||||
element.setAttribute(eventName, 'return;');
|
||||
isSupported = typeof (<any>element)[eventName] === 'function';
|
||||
}
|
||||
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
function syncEvent(node: Element, eventName: string, newEventHandler: (e: Event) => any) {
|
||||
const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
|
||||
const eventStore = (node as any).__events || ((node as any).__events = {});
|
||||
const oldEventHandler = eventStore[eventNameLc];
|
||||
const oldEventHandler = eventStore[eventName];
|
||||
|
||||
// Remove old listener so they don't double up.
|
||||
if (oldEventHandler) {
|
||||
node.removeEventListener(eventNameLc, oldEventHandler);
|
||||
node.removeEventListener(eventName, oldEventHandler);
|
||||
}
|
||||
|
||||
// Bind new listener.
|
||||
if (newEventHandler) {
|
||||
node.addEventListener(eventNameLc, eventStore[eventNameLc] = function handler(e: Event) {
|
||||
node.addEventListener(eventName, eventStore[eventName] = function handler(e: Event) {
|
||||
newEventHandler.call(this, e);
|
||||
});
|
||||
}
|
||||
@ -35,7 +50,12 @@ export function attachEventProps<E extends HTMLElement>(node: E, props: any) {
|
||||
}
|
||||
|
||||
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
||||
syncEvent(node, name.substring(2), props[name]);
|
||||
const eventName = name.substring(2);
|
||||
const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
|
||||
|
||||
if (!isCoveredByReact(eventNameLc)) {
|
||||
syncEvent(node, eventNameLc, props[name]);
|
||||
}
|
||||
} else {
|
||||
(node as any)[name] = props[name];
|
||||
}
|
||||
|
@ -1,26 +1,5 @@
|
||||
import { addIcons } from 'ionicons';
|
||||
import { ICON_PATHS } from 'ionicons/icons';
|
||||
import { IonicConfig } from '@ionic/core';
|
||||
import { defineCustomElements } from '@ionic/core/loader';
|
||||
|
||||
export * from './components';
|
||||
|
||||
export interface IonicGlobal {
|
||||
config?: any;
|
||||
ael?: (elm: any, eventName: string, cb: (ev: Event) => void, opts: any) => void;
|
||||
raf?: (ts: number) => void;
|
||||
rel?: (elm: any, eventName: string, cb: (ev: Event) => void, opts: any) => void;
|
||||
}
|
||||
export * from './types';
|
||||
|
||||
export interface IonicWindow extends Window {
|
||||
Ionic: IonicGlobal;
|
||||
}
|
||||
|
||||
export function registerIonic(config: IonicConfig = {}) {
|
||||
const win: IonicWindow = window as any;
|
||||
const Ionic = (win.Ionic = win.Ionic || {});
|
||||
addIcons(ICON_PATHS);
|
||||
|
||||
Ionic.config = config;
|
||||
defineCustomElements(window);
|
||||
}
|
||||
export * from './register';
|
||||
|
14
react/src/register.ts
Normal file
14
react/src/register.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { addIcons } from 'ionicons';
|
||||
import { ICON_PATHS } from 'ionicons/icons';
|
||||
import { IonicConfig } from '@ionic/core';
|
||||
import { defineCustomElements } from '@ionic/core/loader';
|
||||
import { IonicWindow } from './types';
|
||||
|
||||
export function registerIonic(config: IonicConfig = {}) {
|
||||
const win: IonicWindow = window as any;
|
||||
const Ionic = (win.Ionic = win.Ionic || {});
|
||||
addIcons(ICON_PATHS);
|
||||
|
||||
Ionic.config = config;
|
||||
defineCustomElements(window);
|
||||
}
|
@ -5,6 +5,18 @@ export interface OverlayComponentElement extends HTMLStencilElement {
|
||||
'present': () => Promise<void>;
|
||||
'dismiss': (data?: any, role?: string | undefined) => Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface OverlayControllerComponentElement<E extends OverlayComponentElement> extends HTMLStencilElement {
|
||||
'create': (opts: any) => Promise<E>;
|
||||
}
|
||||
|
||||
export interface IonicGlobal {
|
||||
config?: any;
|
||||
ael?: (elm: any, eventName: string, cb: (ev: Event) => void, opts: any) => void;
|
||||
raf?: (ts: number) => void;
|
||||
rel?: (elm: any, eventName: string, cb: (ev: Event) => void, opts: any) => void;
|
||||
}
|
||||
|
||||
export interface IonicWindow extends Window {
|
||||
Ionic: IonicGlobal;
|
||||
}
|
Reference in New Issue
Block a user