fix(react): nav will remove components from the DOM (#25763)

Issue #: resolves #25753

----------

<!-- Please refer to our contributing documentation for any questions on
submitting a pull request, or let us know here if you need any help:
https://ionicframework.com/docs/building/contributing -->

## Pull request checklist

Please check if your PR fulfills the following requirements:
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been reviewed and added / updated if needed (for bug
fixes / features)
- Some docs updates need to be made in the `ionic-docs` repo, in a
separate PR. See the [contributing
guide](https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation)
for details.
- [x] Build (`npm run build`) was run locally and any changes were
pushed
- [x] Lint (`npm run lint`) has passed locally and any fixes were made
for failures


## Pull request type

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

<!-- Please try to limit your pull request to one type, submit multiple
pull requests if needed. -->

Please check the type of change your PR introduces:
- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe): 


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

When `IonNav` performs a pop operation (navigating to root, back, etc.)
the views are not removed from the DOM.

<!-- Issues are required for both bug fixes and features. -->


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

- `IonNav` removes pages from the DOM when they are popped (navigate
back, navigate to root, etc.)
- Memoized constructing React delegate (was reconstructing on each
render)

## 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. -->

---------

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
Sean Perkins
2023-04-19 13:03:24 -04:00
committed by GitHub
parent 6f910576e2
commit beb46bf9de
3 changed files with 15 additions and 7 deletions

View File

@ -1,6 +1,6 @@
import type { FrameworkDelegate, JSX } from '@ionic/core/components';
import { defineCustomElement } from '@ionic/core/components/ion-nav.js';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { ReactDelegate } from '../../framework-delegate';
import { createReactComponent } from '../react-component-lib';
@ -25,10 +25,10 @@ const IonNavInternal: React.FC<IonNavProps> = ({ children, forwardedRef, ...rest
* Allows us to create React components that are rendered within
* the context of the IonNav component.
*/
const addView = (view: React.ReactElement) => setViews([...views, view]);
const removeView = (view: React.ReactElement) => setViews(views.filter((v) => v !== view));
const addView = (view: React.ReactElement) => setViews((existingViews) => [...existingViews, view]);
const removeView = (view: React.ReactElement) => setViews((existingViews) => existingViews.filter((v) => v !== view));
const delegate = ReactDelegate(addView, removeView);
const delegate = useMemo(() => ReactDelegate(addView, removeView), []);
return (
<IonNavInner delegate={delegate} ref={forwardedRef} {...restOfProps}>

View File

@ -9,7 +9,7 @@ export const ReactDelegate = (
addView: (view: React.ReactElement) => void,
removeView: (view: React.ReactElement) => void
): FrameworkDelegate => {
const refMap = new WeakMap<ReactComponent, React.ReactElement>();
const refMap = new WeakMap<HTMLElement, React.ReactElement>();
const attachViewToDom = async (
parentElement: HTMLElement,
@ -24,17 +24,19 @@ export const ReactDelegate = (
const componentWithProps = component(propsOrDataObj);
const hostComponent = createPortal(componentWithProps, div);
refMap.set(component, hostComponent);
refMap.set(div, hostComponent);
addView(hostComponent);
return Promise.resolve(div);
};
const removeViewFromDom = (_container: any, component: ReactComponent): Promise<void> => {
const removeViewFromDom = (_container: any, component: HTMLElement): Promise<void> => {
const hostComponent = refMap.get(component);
hostComponent && removeView(hostComponent);
component.remove();
return Promise.resolve();
};