fix(react): avoid multiple invocations of onDidDismiss and onWillPresent (#28020)

Issue number: Resolves #28010

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

`onDidDismiss` and `onWillPresent` will fire twice when having a manual
binding in your implementation for inline overlays.

e.g.:
```tsx
<IonAlert onDidDismiss={() => console.log('hello world')} />
```

Will result in:
> hello world
> hello world

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- `onDidDismiss` and `onWillPresent` do not execute the callback handler
twice per invocation

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev-build: `7.3.1-dev.11692305436.16a4008f`
This commit is contained in:
Sean Perkins
2023-08-22 10:23:04 -04:00
committed by GitHub
parent 5a1bbc9fa3
commit 0ac3df3f37
3 changed files with 30 additions and 6 deletions

View File

@@ -63,7 +63,14 @@ export const createInlineOverlayComponent = <PropType, ElementType>(
componentDidUpdate(prevProps: IonicReactInternalProps<PropType>) {
const node = this.ref.current! as HTMLElement;
attachProps(node, this.props, prevProps);
/**
* onDidDismiss and onWillPresent have manual implementations that
* will invoke the original handler. We need to filter those out
* so they don't get attached twice and called twice.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { onDidDismiss, onWillPresent, ...cProps } = this.props;
attachProps(node, cProps, prevProps);
}
componentWillUnmount() {

View File

@@ -1,10 +1,11 @@
import React from 'react';
import { IonButton, IonContent, IonPage, IonActionSheet } from '@ionic/react';
import { useState } from 'react';
import React, { useState } from 'react';
const ActionSheetComponent: React.FC = () => {
const [message, setMessage] = useState('');
const [show, setShow] = useState(false);
const [willPresentCount, setWillPresentCount] = useState(0);
const [didDismissCount, setDidDismissCount] = useState(0);
return (
<IonPage>
@@ -26,7 +27,13 @@ const ActionSheetComponent: React.FC = () => {
},
]}
header="Action Sheet"
onDidDismiss={() => setShow(false)}
onWillPresent={() => {
setWillPresentCount(willPresentCount + 1);
}}
onDidDismiss={() => {
setDidDismissCount(didDismissCount + 1);
setShow(false);
}}
/>
<IonButton expand="block" onClick={() => setShow(true)}>
Show ActionSheet
@@ -41,6 +48,8 @@ const ActionSheetComponent: React.FC = () => {
Show ActionSheet, hide after 250 mss
</IonButton>
<div>{message}</div>
<div>onWillPresent count: {willPresentCount}</div>
<div>onDidDismiss count: {didDismissCount}</div>
</IonContent>
</IonPage>
);

View File

@@ -17,11 +17,19 @@ describe('IonActionSheet', () => {
});
it('display action and call dismiss to close it', () => {
//show action sheet
cy.get('ion-content').contains('onWillPresent count: 0');
cy.get('ion-content').contains('onDidDismiss count: 0');
// show action sheet
cy.get('ion-button').contains('Show ActionSheet, hide after 250 ms').click();
cy.get('ion-action-sheet').contains('Action Sheet');
//verify action sheet is hidden
// verify action sheet is hidden
cy.get('ion-action-sheet').should('not.be.visible');
// verify lifecycle events are called once
cy.get('ion-content').contains('onWillPresent count: 1');
cy.get('ion-content').contains('onDidDismiss count: 0');
});
});