在销毁对象之前取消上传请求会使 mobx-state-tree 抛出 Cannot modify [dead] 错误

Cancelling upload request before destroying object makes mobx-state-tree throw Cannot modify [dead] errors

我有一个 React Native 应用程序,我想在其中使用 Axios 上传一些文件。

我做了一个文件上传的mobx-state-tree存储,每个文件都有自己的CancelTokenSource,发送到axios网络调用

上传正在进行时,我尝试取消上传,然后销毁该项目。

最简单的方法就像我在下面展示的那样,通过销毁商店中的物品,然后有一个取消上传的 beforeDestroy() 挂钩。但是这种方法使 mobx-state-tree 在屏幕截图中显示错误。

我也试过在销毁项目之前明确调用 file.cancelTokenSource.cancel()。同样的错误。我怀疑 cancel() returns 时操作没有完全取消,但由于它不是异步函数,我无法 await 它完成。

当我只是调用 cancel() 而不破坏时,它取消得很好,所以我很确定这是一个时间问题,destroy(file) 在 [= 之前​​被调用得太早了15=] 已自行清理。

在这里做什么?

文件上传-store.ts

import { destroy, flow, Instance, types } from 'mobx-state-tree'

import { FileUpload, IFileUpload } from '../entities/file-upload/file-upload'
import { getApi } from '../../store-environment'

/**
 * Store for handling the FileUpload
 */
export const FileUploadStore = types
  .model('FileUploadStore')
  .props({
    files: types.array(FileUpload),
  })
  .actions((self) => {
    const api = getApi(self)

    const add = (uri: string, name: string, type: string, size: number) => {
      const file = FileUpload.create({
        uri,
        name,
        type,
        size,
      })
      self.files.push(file)
      upload(file)
    }

    const remove = (file: IFileUpload) => {
      destroy(file)
    }

    const cancel = (file: IFileUpload) => {
      // also tried this - with no luck
      // file.cancelTokenSource.cancel()
      destroy(file)
    }

    const upload = flow(function* (file: IFileUpload) {
      file.status = 'pending'
      file.uploadedBytes = 0
      const { uri, name, type } = file

      try {
        const id = yield api.uploadFile(uri, name, type, file.setProgress, file.cancelTokenSource.token)
        file.status = 'completed'
        file.fileUploadId = id
      } catch (error) {
        file.status = 'failed'
        file.error = error.message
      }
    })

    return {
      afterCreate() {
        // Avoid persistance
        self.files.clear()
      },
      remove,
      cancel,
      retry: upload,
      add,
    }
  })

export type IFileUploadStore = Instance<typeof FileUploadStore>

文件-upload.ts

import { Instance, SnapshotIn, types } from 'mobx-state-tree'
import { CancelToken } from 'apisauce'

/**
 * FileUpload contains the particular data of a file, and some flags describing its status.
 */
export const FileUpload = types
  .model('FileUpload')
  .props({
    name: types.string,
    type: types.string,
    uri: types.string,
    size: types.number,
    // set if an arror occours
    error: types.maybe(types.string),
    status: types.optional(types.enumeration(['pending', 'completed', 'failed']), 'pending'),
    // updated by progressCallback
    uploadedBytes: types.optional(types.number, 0),
    // assigned when response from backend is received
    fileUploadId: types.maybe(types.string),
  })
  .volatile(() => ({
    cancelTokenSource: CancelToken.source(),
  }))
  .actions((self) => ({
    setProgress(event: ProgressEvent) {
      self.uploadedBytes = event.loaded
    },
    beforeDestroy() {
      self.cancelTokenSource?.cancel()
    },
  }))

export interface IFileUpload extends Instance<typeof FileUpload> {}
// SnapshotIn, used for creating input to store: {Model}.create({})
export interface IFileUploadSnapshotIn extends SnapshotIn<typeof FileUpload> {}

你正在销毁FileUpload节点并很好地取消axios请求,但是取消请求会抛出错误,所以你需要确保你的FileUpload节点是在您尝试在 catch.

中更新它之前仍然存在
import { destroy, flow, Instance, types, isAlive } from 'mobx-state-tree'

// ...

const upload = flow(function* (file: IFileUpload) {
  const { uri, name, type } = file

  file.status = "pending"
  file.uploadedBytes = 0

  try {
    const id = yield api.uploadFile(
      uri,
      name,
      type,
      file.setProgress,
      file.cancelTokenSource.token
    )

    file.status = "completed"
    file.fileUploadId = id
  } catch (error) {
    if (isAlive(file)) {
      file.status = "failed"
      file.error = error.message
    }
  }
})