fix(react): handle undefined attributes better, fixes #19563, #19580 (#19588)

This commit is contained in:
Ely Lucas
2019-10-09 10:36:25 -06:00
committed by GitHub
parent 7ce916cc7c
commit 033abe994b
10 changed files with 22 additions and 37 deletions

View File

@ -1,14 +0,0 @@
import * as React from 'react';
import { deprecationWarning } from '../utils';
export class ViewManager extends React.Component<{}, {}> {
componentDidMount() {
deprecationWarning('As of @ionic/react RC2, ViewManager is no longer needed and can be removed. This component is now deprecated will be removed from @ionic/react final.');
}
render() {
return this.props.children;
}
}

View File

@ -1,3 +1,2 @@
export { IonReactRouter } from './Router'; export { IonReactRouter } from './Router';
export { ViewManager } from './ViewManager';

View File

@ -32,12 +32,12 @@ describe('syncEvent', () => {
}) })
}); });
describe('attachEventProps', () => { describe('attachProps', () => {
it('should pass props to a dom node', () => { it('should pass props to a dom node', () => {
const onIonClickCallback = () => {}; const onIonClickCallback = () => {};
var div = document.createElement("div"); var div = document.createElement("div");
utils.attachEventProps(div, { utils.attachProps(div, {
'children': [], 'children': [],
'style': 'color: red', 'style': 'color: red',
'ref': () => {}, 'ref': () => {},

View File

@ -4,8 +4,7 @@ import ReactDom from 'react-dom';
import { NavContext } from '../contexts/NavContext'; import { NavContext } from '../contexts/NavContext';
import { RouterDirection } from './hrefprops'; import { RouterDirection } from './hrefprops';
import { attachEventProps, createForwardRef, dashToPascalCase, isCoveredByReact } from './utils'; import { attachProps, createForwardRef, dashToPascalCase, isCoveredByReact } from './utils';
import { deprecationWarning } from './utils/dev';
interface IonicReactInternalProps<ElementType> extends React.HTMLAttributes<ElementType> { interface IonicReactInternalProps<ElementType> extends React.HTMLAttributes<ElementType> {
forwardedRef?: React.Ref<ElementType>; forwardedRef?: React.Ref<ElementType>;
@ -29,16 +28,11 @@ export const createReactComponent = <PropType, ElementType>(
componentDidMount() { componentDidMount() {
this.componentDidUpdate(this.props); this.componentDidUpdate(this.props);
if (this.props.href) {
setTimeout(() => {
deprecationWarning('hrefchange', 'As of RC3, href links no longer go through the router, so transitions will not be applied to these links. To maintain transitions, use the new routerLink prop.');
}, 2000);
}
} }
componentDidUpdate(prevProps: IonicReactInternalProps<PropType>) { componentDidUpdate(prevProps: IonicReactInternalProps<PropType>) {
const node = ReactDom.findDOMNode(this) as HTMLElement; const node = ReactDom.findDOMNode(this) as HTMLElement;
attachEventProps(node, this.props, prevProps); attachProps(node, this.props, prevProps);
} }
private handleClick = (e: React.MouseEvent<PropType>) => { private handleClick = (e: React.MouseEvent<PropType>) => {

View File

@ -1,7 +1,7 @@
import { OverlayEventDetail } from '@ionic/core'; import { OverlayEventDetail } from '@ionic/core';
import React from 'react'; import React from 'react';
import { attachEventProps } from './utils'; import { attachProps } from './utils';
interface OverlayBase extends HTMLElement { interface OverlayBase extends HTMLElement {
present: () => Promise<void>; present: () => Promise<void>;
@ -57,7 +57,7 @@ export const createControllerComponent = <OptionsType extends object, OverlayTyp
const overlay = this.overlay = await controller.create({ const overlay = this.overlay = await controller.create({
...cProps as any ...cProps as any
}); });
attachEventProps(overlay, { attachProps(overlay, {
[dismissEventName]: onDidDismiss [dismissEventName]: onDidDismiss
}, prevProps); }, prevProps);
await overlay.present(); await overlay.present();

View File

@ -2,7 +2,7 @@ import { OverlayEventDetail } from '@ionic/core';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { attachEventProps } from './utils'; import { attachProps } from './utils';
interface OverlayElement extends HTMLElement { interface OverlayElement extends HTMLElement {
present: () => Promise<void>; present: () => Promise<void>;
@ -68,7 +68,7 @@ export const createOverlayComponent = <T extends object, OverlayType extends Ove
componentProps: {} componentProps: {}
}); });
attachEventProps(overlay, elementProps, prevProps); attachProps(overlay, elementProps, prevProps);
await overlay.present(); await overlay.present();
} }

View File

@ -3,5 +3,5 @@ import { JSX } from '@ionic/core';
import { /*@__PURE__*/ createReactComponent } from './createComponent'; import { /*@__PURE__*/ createReactComponent } from './createComponent';
export const IonTabBarInner = /*@__PURE__*/createReactComponent<JSX.IonTabBar, HTMLIonTabBarElement>('ion-tab-bar'); export const IonTabBarInner = /*@__PURE__*/createReactComponent<JSX.IonTabBar, HTMLIonTabBarElement>('ion-tab-bar');
export const IonBackButtonInner = /*@__PURE__*/createReactComponent<JSX.IonBackButton, HTMLIonBackButtonElement>('ion-back-button'); export const IonBackButtonInner = /*@__PURE__*/createReactComponent<Omit<JSX.IonBackButton, 'icon'>, HTMLIonBackButtonElement>('ion-back-button');
export const IonRouterOutletInner = /*@__PURE__*/createReactComponent<JSX.IonRouterOutlet, HTMLIonRouterOutletElement>('ion-router-outlet'); export const IonRouterOutletInner = /*@__PURE__*/createReactComponent<JSX.IonRouterOutlet, HTMLIonRouterOutletElement>('ion-router-outlet');

View File

@ -5,7 +5,11 @@ import { NavContext } from '../../contexts/NavContext';
import { IonicReactProps } from '../IonicReactProps'; import { IonicReactProps } from '../IonicReactProps';
import { IonBackButtonInner } from '../inner-proxies'; import { IonBackButtonInner } from '../inner-proxies';
type Props = LocalJSX.IonBackButton & IonicReactProps & { type Props = Omit<LocalJSX.IonBackButton, 'icon'> & IonicReactProps & {
icon?: {
ios: string;
md: string;
};
ref?: React.RefObject<HTMLIonBackButtonElement>; ref?: React.RefObject<HTMLIonBackButtonElement>;
}; };

View File

@ -1,6 +1,6 @@
import { camelToDashCase } from './case'; import { camelToDashCase } from './case';
export const attachEventProps = (node: HTMLElement, newProps: any, oldProps: any = {}) => { export const attachProps = (node: HTMLElement, newProps: any, oldProps: any = {}) => {
// add any classes in className to the class list // add any classes in className to the class list
const className = getClassName(node.classList, newProps, oldProps); const className = getClassName(node.classList, newProps, oldProps);
if (className !== '') { if (className !== '') {
@ -19,10 +19,12 @@ export const attachEventProps = (node: HTMLElement, newProps: any, oldProps: any
syncEvent(node, eventNameLc, newProps[name]); syncEvent(node, eventNameLc, newProps[name]);
} }
} else { } else {
if (typeof newProps[name] === 'object') {
(node as any)[name] = newProps[name]; (node as any)[name] = newProps[name];
} else { const propType = typeof newProps[name];
if (propType === 'string') {
node.setAttribute(camelToDashCase(name), newProps[name]); node.setAttribute(camelToDashCase(name), newProps[name]);
} else {
(node as any)[name] = newProps[name];
} }
} }
}); });
@ -69,7 +71,7 @@ export const isCoveredByReact = (eventNameSuffix: string, doc: Document = docume
return isSupported; return isSupported;
}; };
export const syncEvent = (node: Element & {__events?: {[key: string]: ((e: Event) => any) | undefined}}, eventName: string, newEventHandler?: (e: Event) => any) => { export const syncEvent = (node: Element & { __events?: { [key: string]: ((e: Event) => any) | undefined } }, eventName: string, newEventHandler?: (e: Event) => any) => {
const eventStore = node.__events || (node.__events = {}); const eventStore = node.__events || (node.__events = {});
const oldEventHandler = eventStore[eventName]; const oldEventHandler = eventStore[eventName];

View File

@ -14,7 +14,7 @@ export const createForwardRef = <PropType, ElementType>(ReactComponent: any, dis
return React.forwardRef(forwardRef); return React.forwardRef(forwardRef);
}; };
export * from './attachEventProps'; export * from './attachProps';
export * from './case'; export * from './case';
export const isPlatform = (platform: Platforms) => { export const isPlatform = (platform: Platforms) => {