Read the original article:How to Resolve Data Inconsistency Between Parent and Child Components
Context
Variables decorated with @Prop establish a one-way synchronization relationship with the parent component. When the data source changes, the variables decorated with @Prop will update and override all local changes. Therefore, the synchronization of values is from the parent component to the child component, and changes in the child component's values will not be synchronized back to the parent component.
LocalStorage is an in-memory "database" provided by ArkTS for storing page-level state variables. Applications can create multiple LocalStorage instances, which can be shared within a page or across pages and UIAbility instances through the getShared interface.
Variables decorated with @Link establish a two-way data binding with the corresponding data source in their parent component. When one side changes, the other side will also be updated synchronously.
Description
Communication between parent and child components involves the parent component using the @State decorator to modify a variable and provide a default cached value, while the child component uses @Prop to receive values passed from the parent component. The parent component initiates an RPC request in the aboutToAppear lifecycle method. When the requested value has not yet returned, the child component uses the cached value from the parent component. Once the RPC request value is returned, the child component synchronously updates with the returned value. However, a breakpoint reveals that after the parent component's RPC request value is returned, the value is not synchronously refreshed in the child component's aboutToAppear lifecycle method. Is there a way for @Prop to synchronously update, or are there other methods for synchronous updates?
Solution
The reason for the phenomenon is that the parent component's RPC request did not return before the child component's aboutToAppear was executed.
Option One:
The issue can be resolved by creating a page-level UI state storage through LocalStorage. Since LocalStorage supports state sharing among multiple pages within a UIAbility instance, the parameters received by the @Entry decorator can share the same LocalStorage instance across pages. The steps are as follows:
1.Create a LocalStorage instance.
let localStorageA: LocalStorage = new LocalStorage();
2.The setOrCreate method of the LocalStorage instance sets the cached value.
localStorageA.setOrCreate('isPT', true);
3.The parent component passes the LocalStorage instance to the child component.
@Builder
PageMap(name: string) {
if (name === 'pageOne') {
pageOneStack({}, localStorageA)
}
}
4.In the child component, bidirectional data synchronization is established between the property corresponding to the key in LocalStorage and the key using @LocalStorageLink(key).
@LocalStorageLink('isPT') isPT: boolean = true;
5.The parent component's aboutToAppear simulates an RPC asynchronous request.
aboutToAppear(): void {
setTimeout(() => {
localStorageA.setOrCreate('isPT', false)
},2000)
}
The complete example is as follows:
let localStorageA: LocalStorage = new LocalStorage();
localStorageA.setOrCreate('isPT', true);
@Entry
@Component
struct MyNavigationTestStack {
@Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();
@Builder
PageMap(name: string) {
if (name === 'pageOne') {
pageOneStack({}, localStorageA)
}
}
aboutToAppear(): void {
setTimeout(() => {
localStorageA.setOrCreate('isPT', false)
}, 5000)
}
build() {
Column({ space: 5 }) {
Navigation(this.pageInfo) {
Column() {
Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
this.pageInfo.pushPath({ name: 'pageOne' });
})
}
}
.title('NavIndex')
.navDestination(this.PageMap)
.mode(NavigationMode.Stack)
.borderWidth(1)
}
}
}
@Component
struct pageOneStack {
@Consume('pageInfo') pageInfo: NavPathStack;
@LocalStorageLink('isPT') isPT: boolean = true;
build() {
NavDestination() {
Column() {
NavigationContentMsgStack()
Text(`${this.isPT}`)
}.width('100%').height('100%')
}
.title('pageOne')
.onBackPressed(() => {
this.pageInfo.pop();
return true;
})
}
}
@Component
struct NavigationContentMsgStack {
@LocalStorageLink('isPT') isPT: boolean = true
build() {
Column() {
Text(`isPT: ${this.isPT}`)
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
}
}
Option Two:
Use a state variable decorated with @Link to receive the value passed from the parent component.
The complete code is as follows:
@Entry
@Component
struct MyNavigationTestStack {
pageInfo: NavPathStack = new NavPathStack();
@State isPT: boolean = true
@Builder
PageMap(name: string) {
if (name === 'pageOne') {
pageOneStack({ isPT: this.isPT })
}
}
aboutToAppear(): void {
setTimeout(() => {
this.isPT = false
}, 5000)
}
build() {
Column({ space: 5 }) {
Navigation(this.pageInfo) {
Column() {
Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
this.pageInfo.pushPath({ name: 'pageOne' });
})
}
}
.title('NavIndex')
.mode(NavigationMode.Stack)
.borderWidth(1)
.navDestination(this.PageMap)
}
}
}
@Component
struct pageOneStack {
pageInfo: NavPathStack = new NavPathStack()
@Link isPT: boolean
build() {
NavDestination() {
Column() {
NavigationContentMsgStack({ isPT: this.isPT })
Text(`${this.isPT}`)
}.width('100%').height('100%')
}
.title('pageOne')
.onReady((value) => {
this.pageInfo = value.pathStack
})
.onBackPressed(() => {
this.pageInfo.pop();
return true;
})
}
}
@Component
struct NavigationContentMsgStack {
@Link isPT: boolean
build() {
Column() {
Text(`isPT: ${this.isPT}`)
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
}
}
Key Takeaways
- LocalStorage supports state sharing among multiple pages within a UIAbility instance. Use it to share data.
- Or, use a state variable decorated with
@Linkto receive the value passed from the parent component.
Top comments (0)