DEV Community

HarmonyOS
HarmonyOS

Posted on

After using replacePath for navigation, how to carry data back to the previous page in the stack?

Read the original article:After using replacePath for navigation, how to carry data back to the previous page in the stack?

After using replacePath for navigation, how to carry data back to the previous page in the stack?

Requirement Description

When PageA navigates to PageB using pushPath, and then PageB navigates forward again using replacePath, how can PageB pass data back to PageA when returning to it?

image.png

Background Knowledge

· pushPath: Pushes the specified NavDestination onto the stack. Behavior can be adjusted via NavigationOptions.

· NavPathInfo: Route/page information object.

· replacePath: Pops the current top of the stack and pushes the specified NavDestination.

· onPop: Callback that receives a return value when a stacked page is popped.

· onResult: Callback on a NavDestination that fires when the destination receives a result upon return.

Implementation Steps

Approach 1 — Pass a reusable **onPop** handler

  1. In PageA, define a generic onPop handler function that processes returned data.
  2. When calling pushPath to PageB, pass this handler (e.g., via params/context).
  3. In PageB, before calling replacePath, bind onPop to the incoming handler so that when PageB is popped (or the stack changes), the data is routed back to PageA.

Approach 2 — Use onResult on the destination

  1. In PageA’s NavDestination, register onResult to listen for results when control returns to PageA.
  2. In PageB, when completing work and calling replacePath, emit/attach the payload so that PageA’s onResult receives it.

Code Snippet / Configuration

Approach 1:

// Navigation Page
import { PageA } from './PageA'
import { PageB } from './PageB'

@Entry
@Component
struct Index {
  @Provide pathStack: NavPathStack = new NavPathStack()

  @Builder
  pageMap(params: string) {
    if (params === 'PageA') {
      PageA()
    } else if (params === 'PageB') {
      PageB()
    }
  }

  build() {
    Navigation(this.pathStack) {
      Column() {
        Button('Push PageA')
          .onClick(() => {
            this.pathStack.pushPathByName('PageA', null)
          })
      }.width('100%').height('100%')
      .justifyContent(FlexAlign.Center)
    }.navDestination(this.pageMap)
  }
}
Enter fullscreen mode Exit fullscreen mode
// PageA page
@ObservedV2
export class Params {
  @Trace num: number = 0
  click: (popInfo: PopInfo) => void = () => {
  }
}

@Component
export struct PageA {
  @Consume pathStack: NavPathStack;
  private param: Params = new Params()
  // The onPop callback in the encapsulation
  customOnPop = (popInfo: PopInfo) => {
    this.param.num = popInfo.result as number
  }

  aboutToAppear(): void {
   // Initialization Parameters
    this.param.num = 0
    this.param.click = this.customOnPop
  }

  build() {
    NavDestination() {
      Column() {
        Column() {
          Text('One Page')
            .fontSize('30')
          Text(`${this.param.num}`)
            .fontSize('30')
        }
        .justifyContent(FlexAlign.Center)
        .size({ width: '100%', height: '60%' })

        Row() {
          Button('push PageB')
            .onClick(() => {
              this.pathStack.pushPath({
                name: 'PageB', param: this.param, onPop: (popInfo) => {
                  this.param.click(popInfo) // Use the encapsulated onPop callback
                }
              })
            })
        }.justifyContent(FlexAlign.SpaceAround)
        .alignItems(VerticalAlign.Center)
        .size({ width: '100%', height: '40%' })
      }
    }.width('100%').height('100%')
  }
}
// PageB page
import { Params } from './PageA';

@Component
export struct PageB {
  @Consume pathStack: NavPathStack;
  private param: Params = new Params()

  build() {
    NavDestination() {
      Column() {
        Row() {
          Text('Second Page')
            .fontSize('30')
        }
        .justifyContent(FlexAlign.Center)
        .alignItems(VerticalAlign.Center)
        .size({ width: '100%', height: '60%' })

        Column() {
          Button('Replace PageB')
            .onClick(() => {
              this.pathStack.replacePath({
                name: 'PageB', onPop: (popInfo) => {
                  this.param.click(popInfo) // Use the passed method
                }
              })
            })
          Button('pop back')
            .onClick(() => {
              this.pathStack.pop(10)
            })
        }.size({ width: '100%', height: '40%' })
      }
    }.width('100%').height('100%')
    .onReady((ctx: NavDestinationContext) => {
      this.param = ctx?.pathInfo?.param as Params // Receive the passed parameters
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Approach 2:

// PageA page
@ObservedV2
export class Params {
  @Trace num: number = 0
}

@Component
export struct PageA {
  @Consume pathStack: NavPathStack;
  private param: Params = new Params()

  aboutToAppear(): void {
    this.param.num = 0
  }

  build() {
    NavDestination() {
      Column() {
        Column() {
          Text('One Page')
            .fontSize('30')
          Text(`${this.param.num}`)
            .fontSize('30')
        }.justifyContent(FlexAlign.Center)
        .size({ width: '100%', height: '60%' })

        Row() {
          Button('push PageB')
            .onClick(() => {
              this.pathStack.pushPath({ name: 'PageB' })
            })
        }.justifyContent(FlexAlign.SpaceAround)
        .alignItems(VerticalAlign.Center)
        .size({ width: '100%', height: '40%' })
      }
    }
    .onResult((num: number) => {
      this.param.num = num // Receive parameters carried back to this page
    }).size({ width: '100%', height: '100%' })
  }
}
// PageB page
@Component
export struct PageB {
  @Consume pathStack: NavPathStack;

  build() {
    NavDestination() {
      Column() {
        Row() {
          Text('Second Page')
            .fontSize('30')
        }
        .justifyContent(FlexAlign.Center)
        .alignItems(VerticalAlign.Center)
        .size({ width: '100%', height: '60%' })

        Column() {
          Button('Replace PageB')
            .onClick(() => {
              this.pathStack.replacePath({ name: 'PageB' })
            })

          Button('pop back')
            .onClick(() => {
              this.pathStack.pop(10)
            })
        }.size({ width: '100%', height: '40%' })
      }
    }.size({ width: '100%', height: '100%' })
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

· Verify that when PageB navigates via replacePath, PageA:

o Receives the correct payload via the passed onPop handler (Approach 1), or

o Triggers onResult with the expected data (Approach 2).

· Confirm no duplicate pages remain on the stack after replacePath.

img

Limitations or Considerations

· pop cannot reset param values. If you need to reset target-page params for singleton pages, use pushPath with appropriate launchMode and new params:

o MOVE_TO_TOP_SINGLETON: Searches bottom→top; if the named destination exists, move it to top.

o POP_TO_SINGLETON: Searches bottom→top; if the named destination exists, remove everything above it.

· FAQ embedded

o Q: How to reset target-page params when navigating to a singleton page?

§ A: Use pushPath with launchMode: POP_TO_SINGLETON or MOVE_TO_TOP_SINGLETON and pass new params (behaves like pop but allows param reset).

this.pageInfos.pushPath({ name: 'pageOne', param: '' }, { launchMode: LaunchMode.POP_TO_SINGLETON })
Enter fullscreen mode Exit fullscreen mode

o Q: Is there a HarmonyOS equivalent of Android’s Activity.onNewIntent for singleton pages?

§ A: Use onNewParam. It fires when an existing NavDestination becomes top via MOVE_TO_TOP_SINGLETON or POP_TO_SINGLETON, making it suitable for handling updated parameters.

.onNewParam((info: string) => {
  console.info(`onNewParam: ${info}`)
})
Enter fullscreen mode Exit fullscreen mode

Related Documents or Links

https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-basic-components-navigation#replacepath11

Written by Hasan Kaya

Top comments (0)