鸿蒙问题之CustomDialog后持久化@state数据崩溃

开发需求:有一个字符串数组,可以通过弹框编辑其中的某个字符串,编辑完成后更新数组并持久化这个数组。

这个需求算是很简单,很常见的需求了。但是,开发过程中却遇到了一个不小的难题。

我的数组内容需要在组件中显示,数据更新,页面刷新。所以,用@state装饰器修饰数组。

然后,点击某个字符串内容弹出customDialog,用户修改,确认后将修改的字符串回传,同时,修改数组的内容,持久化数组。一番操作猛如虎,结果一看直接崩溃

而且这个崩溃从以上日志是真看不出来为啥。不断的删减代码发现一个官方问题。@state修饰的属性,在customDialog回调中利用用户首选项持久化会崩溃,至于为啥,估计也就官方知道。

结果有了,贴一下我的崩溃示例代码

import Prompt from '@system.prompt'
import dataPreferences from '@ohos.data.preferences'

@Entry
@Component
struct Index {
  @State currentTitle: string = '点击我'
    //需要持久化的数据
  @State dataArray: string[] = ['654','357','65456','35723']
  private templateDataManager = new BarrageTemplateModel()

//自定义弹框
  editDialogController: CustomDialogController = new CustomDialogController({
    builder: CustomEditDialog({
      confirm: this.onConfirm.bind(this),
      currentTitle:$currentTitle
    }),
    autoCancel: false,
    alignment: DialogAlignment.Center,
    offset: { dx: 0, dy: -20 },
    gridCount: 4,
    customStyle: false
  })
    
//弹框修改完成回调
  onConfirm() {
    this.dataArray.splice(0,1)     
//这里会导致崩溃   
this.templateDataManager.saveTemplateData(getContext(this),'kUserTemplateKey_HanHua',copy)
  }

  build() {
    Row() {
      Column() {
        Text(this.currentTitle)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            //这样直接操作,不牵扯dialog就不崩溃
            // this.dataArray.splice(0,1)
            // this.templateDataManager.saveTemplateData(getContext(this),'kUserTemplateKey_HanHua',this.dataArray)
            // Prompt.showToast({message:'点击了'})

            //这样会崩溃
            if (this.editDialogController != undefined) {
              this.editDialogController.open()
            }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

//弹出框
@CustomDialog
struct CustomEditDialog {

  @Link currentTitle: string
  private tempTitle: string = ''

  controller: CustomDialogController
  cancel: () => void
  confirm: () => void

  build() {
    Column() {

      TextArea({text: this.currentTitle})
        .backgroundColor(0xffffff)
        .fontColor(Color.Black)
        .fontSize(20)
        .width('100%')
        .height(200)
        .margin(20)
        .onChange((value: string) => { //显示键盘
          if (value) {
            this.tempTitle = value;
          }
        })

      Divider()
        .opacity(0.5)
        .width('100%')
        .color('#D3D3D3')

      Row() {

        Text('取消')
          .backgroundColor(0xffffff)
          .fontColor(Color.Black)
          .fontSize(20)
          .margin(10)
          .height('100%')
          .width('45%')
          .textAlign(TextAlign.Center)
          .onClick(() => {
            this.controller.close()
            this.cancel()
          })

        Divider()
          .vertical(true)
          .color('#D3D3D3')

        Text('确定')
          .backgroundColor(0xffffff)
          .fontColor(Color.Black)
          .fontSize(20)
          .margin(10)
          .width('45%')
          .height('100%')
          .textAlign(TextAlign.Center)
          .onClick(() => {
            this.currentTitle = this.tempTitle
            this.controller.close()
            this.confirm()
          })
      }
      .justifyContent(FlexAlign.SpaceEvenly)
      .width('100%')
      .height(60)
    }
  }
}


//存储数据的类
class BarrageTemplateModel {

  saveTemplateData(context: Context,key: string, hanHuaArray: string[]) {
    console.log('存储用户持久化存储的模版:000='+hanHuaArray.toString())
    dataPreferences.getPreferences(context,"PREFERENCE_KEY",(err,preference) =>{
      if (err) {
        console.log('存储用户持久化存储的模版:err1='+err)
        return
      }
      console.log('存储用户持久化存储的模版:success1=')
      preference.put(key,hanHuaArray,(err) =>{
        if (err) {
          console.log('存储用户持久化存储的模版:err2='+err)
          return
        }
        console.log('存储用户持久化存储的模版:success2=')
        preference.flush((err) => {
          console.log('存储用户持久化存储的模版:success3=')
          if (err) {
            console.log('存储用户持久化存储的模版:err3='+err)
            return
          }
          console.log('存储用户持久化存储的模版:success4=')

          console.log('存储用户持久化存储的模版:'+hanHuaArray.toString())
        })
      })
    })
  }
}

查了不少资料,一开始认为是不是因为用户首选项不支持字符串数组,后来看文档是支持的。

然后,认为是不是因为回调的bind(this),但是不添加bind(this)又会导致回调中的this不是当前组件,访问不到属性。

直到后来注意到我的数组用了@state修饰,去掉后果然不崩溃了。但是我的组件中又需要这个数组用@state修饰。进退两难。。。。

最后只能来个曲线救国,我持久化的数据不直接操作@state的对象,而是持久化copy出来新的一份,就不崩溃了,如下

//弹出框的回调方法
  onConfirm() {
    //假设这是对数组进行操作了
    this.dataArray.splice(0,1)
    //回调中我将dataArray复制存储在copy中
    const copy = this.dataArray.map( num => num )
    //持久化copy数据,不操作dataArray
this.templateDataManager.saveTemplateData(getContext(this),'kUserTemplateKey_HanHua',copy)
  }

这个操作(复制一份,持久化复制的内容)真的辣眼睛,但是这样确实不崩溃了,值得一提的是:如果你直接const copy = this.dataArray也是不行的,除非复制出来新的一块内存。这个问题估计官方会修复,而且这个操作也太常见了。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值