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?
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
- In PageA, define a generic
onPophandler function that processes returned data. - When calling
pushPathto PageB, pass this handler (e.g., via params/context). - In PageB, before calling
replacePath, bindonPopto 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
- In PageA’s
NavDestination, registeronResultto listen for results when control returns to PageA. - In PageB, when completing work and calling
replacePath, emit/attach the payload so that PageA’sonResultreceives 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)
}
}
// 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
})
}
}
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%' })
}
}
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.
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 })
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}`)
})

Top comments (0)