HarmonyOS 鸿蒙Next 如何在 ArkTS 中使用父组件传递的插槽,并结合父子组件的状态?

HarmonyOS 鸿蒙Next 如何在 ArkTS 中使用父组件传递的插槽,并结合父子组件的状态? 开放求职 | 远程/南京鸿蒙开发岗位

尊敬的读者,

首先,衷心感谢您对我的博文的持续关注和支持。写作旅程中得到您的认可和反馈,对我而言是莫大的鼓励。目前,我正寻求新的职业发展机会,特别是在远程工作或南京地区的鸿蒙开发岗位。

我对于鸿蒙开发有着浓厚的兴趣和实践经验,热衷于探索和利用这一平台的潜力,为用户创造价值。如果您的组织或您所了解的企业正在寻找充满热情、技术熟练的鸿蒙开发人才,请不吝赐教。

微信号:13956233265

我真诚希望能够通过我的专业技能和热情为贵组织带来新的视角和价值。无论结果如何,我都将深感荣幸能够得到您的考虑和回应。

感谢您抽出宝贵时间阅读此信息。期待有机会与您进一步交流。

最诚挚的,yarnb

在 ArkTS 开发中,组件可以通过定义 builderParams(构建函数参数)来允许父组件自定义视图区域,类似于 Vue 的 slot 插槽或 React 的 Render Props。

具体来说,构建函数内的 this 指向调用它的子组件,官方文档对此有明确说明。以下例子进一步阐释了这一点:

@Component
struct TestBuilder_bind_this_and_update_parent_state_Child {
  @State count: number = 0;

  @Builder builder() {
    Text(`builder: ${this.count}`)
      .fontSize(50)
  }

  @BuilderParam builderParams: () => void = this.builder

  build() {
    Column() {
      Text(`child: ${this.count}`).fontSize(40)
      Button('child: add').onClick(() => {
        this.count++;
      })
      Column() {
        this.builderParams()
      }
      .border({
        width: 1
      })
    }
    .border({
      width: 1
    })
  }
}

@Entry
@Component
struct TestBuilder_bind_this_and_update_parent_state {
  @State count: number = 0;

  @Builder builder() {
    Text(`parent builder: ${this.count}`).fontSize(50)
    Button('parent builder: add').onClick(() => {
      this.count++;
    })
  }

  build() {
    Column() {
      Column() {
        Column() {
          Text(`parent: ${this.count}`).fontSize(40)
          Button('parent: add').onClick(() => {
            this.count++;
          })
        }
        TestBuilder_bind_this_and_update_parent_state_Child({
          builderParams: this.builder,
        }).border({
          width: 1
        })
      }
      .border({
        width: 1
      })

    }
    .size({
      width: '100%',
      height: '100%'
    })
    .justifyContent(FlexAlign.Center)
  }
}

在这个示例中,点击父组件的 add 按钮时,父组件的状态会变化,但不会影响子组件。反之,点击子组件或构建函数中的 add 会相互影响,这与我们的预期一致。

但如果构建函数定义在父组件中,通常情况下,函数内部逻辑会调用组件自身的状态。这种情况下,可以通过 bind 方法将父组件的 this 绑定到构建函数上,如下所示:

builderParams: this.builder.bind(this),

这样,点击父组件的 add 后,插槽和父组件的状态都会发生变化并且得到渲染。

接下来,考虑子组件传递状态到构建函数的场景。这里的问题是,使用 bind 后,子组件的状态变化不会触发构建函数的更新。如下是父组件和子组件的代码修改:

@Builder builder($$: {count: number}) {
    Text(`parent builder count: ${$$.count}`).fontSize(50)
    Text(`parent builder this.count: ${this.count}`).fontSize(50)
    Button('parent builder: add').onClick(() => {
      this.count++;
    })
  }

当修改父组件状态时,因为使用了 bind,父组件和构建函数中的 this 都会变化。

但修改子组件状态时,注入的状态不会更新。

若想使子组件能够控制插槽注入的更新,可以去除 bind。但这样做会导致父组件无法控制构建函数中使用的状态。解决方案是使用 @Link,它无需 bind。子组件通过 link 变量(对象类型)为插槽提供数据接口,父组件可以定义并在构建函数中通过 this 获取这些数据,同时子组件注入的状态变化也能同步更新。

@Component
struct TestBuilder_bind_this_and_update_parent_state_Child {
  @State count: number = 0;
  [@Link](/user/Link) builderLinkData: object

  @Builder builder($$: { count: number }) {
    Text(`builder: ${$$.count}`)
      .fontSize(50)
  }

  @BuilderParam builderParams: ($$: { count: number }) => void = this.builder

  build() {
    Column() {
      Text(`child: ${this.count}`).fontSize(40)
      Button('child: add').onClick(() => {
        this.count++;
      })
      Column() {
        this.builderParams({ count: this.count })
      }
      .border({
        width: 1
      })
    }
    .border({
      width: 1
    })
  }
}

@Entry
@Component
struct TestBuilder_bind_this_and_update_parent_state {
  @State count: number = 0;
  @State builderLinkData: { count: number } = { count: 0 }

  @Builder builder($$: { count: number }) {
    Text(`parent builder count: ${$$.count}`).fontSize(19)
    Text(`parent builder this.count: ${this.count}`).fontSize(19)
    Text(`parent builder builderLinkData.count: ${this.builderLinkData.count}`).fontSize(19)
    Button('parent builder: add').onClick(() => {
      this.count++;
    })
    Button('parent builder link: add').onClick(() => {
      this.builderLinkData.count++;
    })
  }

  build() {
    Column() {
      Column() {
        Column() {
          Text(`parent: ${this.count}`).fontSize(40)
          Button('parent: add').onClick(() => {
            this.count++;
          })
        }

        TestBuilder_bind_this_and_update_parent_state_Child({
          builderParams: this.builder,
          builderLinkData: $builderLinkData
        }).border({
          width: 1
        })
      }
      .border({
        width: 1
      })

    }
    .size({
      width: '100%',
      height: '100%'
    })
    .justifyContent(FlexAlign.Center)
  }
}

更新父组件时,只有父组件状态变化。

更新子组件状态时,注入的参数和 this 指向的 count 都会变化。

父组件可以正常修改 link data,并观察到其更新和变化。

但使用 link 作为组件参数有一个缺点:必须传递。如果插槽数量多,组件的易用性会降低,且子组件内部使用 object 类型的 link 可能会引发错误。

总结:

  1. 父组件想在传给子组件的插槽中使用和修改状态,最简单的办法是在传入构建函数前使用 bind this。
  2. 要在构建函数中使用子组件的状态,并且让子组件控制状态更新,不能使用 bind。若要同时考虑第一点,则需要在子组件暴露 link 参数。这样父组件的构建函数可以使用定义在自身的准备传递给子组件的 link 状态,且不影响子组件的状态更新。由于子组件需要暴露具体类型的 link,这会降低组件的通用性,并且 link 参数必须传入,这也会降低组件易用性,尤其是当插槽数量增多时。

更多关于HarmonyOS 鸿蒙Next 如何在 ArkTS 中使用父组件传递的插槽,并结合父子组件的状态?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS 鸿蒙Next 如何在 ArkTS 中使用父组件传递的插槽,并结合父子组件的状态?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,ArkTS支持通过插槽(Slot)机制实现父子组件之间的内容传递和状态管理。要在ArkTS中使用父组件传递的插槽并结合父子组件的状态,可以按照以下步骤操作:

  1. 定义子组件:在子组件中,通过@Slot装饰器声明插槽,并使用@State@Prop等装饰器管理组件的状态。
@Component
struct ChildComponent {
  @Slot content: () => void;
  @State private childState: string = 'Child State';

  build() {
    Column() {
      Text(this.childState)
      this.content() // 渲染父组件传递的插槽内容
    }
  }
}
  1. 在父组件中使用子组件:父组件通过插槽将内容传递给子组件,并可以结合父组件的状态进行动态渲染。
@Component
struct ParentComponent {
  @State private parentState: string = 'Parent State';

  build() {
    Column() {
      ChildComponent({
        content: () => {
          Text(this.parentState) // 使用父组件的状态
        }
      })
    }
  }
}
  1. 状态联动:通过@Link@Provide/@Consume等装饰器,可以实现父子组件之间的状态联动,确保状态变化时组件能够同步更新。
@Component
struct ParentComponent {
  @State private sharedState: string = 'Shared State';

  build() {
    Column() {
      ChildComponent({
        content: () => {
          Text(this.sharedState) // 共享状态
        }
      })
      Button('Change State').onClick(() => {
        this.sharedState = 'Updated State';
      })
    }
  }
}

通过上述方式,可以在ArkTS中实现父组件传递插槽并结合父子组件的状态管理。

回到顶部