记一笔之:antd 的 Upload 上传组件 uploading 状态踩坑记

相信用 Upload 的很多老兄都遇到过这个问题,就是在处理上传文件时,从 onchange 方法中得到的 e.fileList 里面的 status 参数他么总是 uploading ,而不是我们需要的 done 状态,导致拿不到 thumbUrl 和 response 的 imgUrl 。
根据官网文档上对 这个问题 的解释,就是要注意两点:

  • 其一是要保证 onchange 事件内部,至少有一次无阻碍的调用 setState 赋值 fileList,让所有状态变化都及时更新。当然这个“无阻碍”是我自己的理解,就是说 setState 的方法执行最好不写在 if 这类条件判断里面,直接写在最底部或者最顶部就行;
  • 其二是要保证每次 setState 后 fileList 的状态都是最新的。所以,以上 issue 里面建议上一点我说的写法最好是:
 this.setState({ fileList: [...this.state.fileList] })

不过对于以上第二点,个人亲测,即使直接 { fileList: e.fileList } 就可以。以下假定只传一张图片,一个完整的 onchange 事件内部处理可以像下面一样:

handleChange = e => {
    if (e.file.status == 'done') {
      if (e.file.response.code === 200) {
        let result = e.fileList;
        result[0].thumbUrl = result[0].response.imgUrl; //用绝对路径替代base64,有助于在保存图片的时候避免字段过长和减小数据库的压力
        this.setState({ fileList: result });
      } else {
        Modal.warn({
          title: '提示',
          content: {e.file.response.msg},
          okText: 'ok',
        });
      }
    }
    this.setState({ fileList: e.fileList });
  };

对应的react DOM结构可以是:

          
                {this.props.form.getFieldDecorator('file', { rules: [{ required: true, message: '请上传图片' }] })(
                    
{this.state.fileList.length >= 1 ? null :
上传
}
example
)}

如上DOM结构,在一些常规操作中,上传图片的操作,很可能会存在这样的场景:新建记录、更新记录、获取记录详情、保存记录等等,同时配合使用 Form 以及 Form.Item 组件,以及 getFieldValue、setFieldValue、getFieldDecorator 等等 api 使 Upload 的操作受控。在使用 Upload 组件时,也许需要注意以下几点:

  • 其一:在获取记录详情时,拿到图片的 url,一定要将 state 中的 fileList 的 status 状态设为 done:
          this.setState({
              previewImage: url,
              fileList: [ {
                  thumbUrl: url,
                  status: 'done',    // 这个必须,不然,图片自然显示不出来
                  uid: url,      // 这个随意吧
              } ],
            });
      // 以及将受控字段的值设为 url:
        this.props.form.setFieldsValue( { file: bannerUrl } )
  • 其二:在编辑图片的时候,比如想将图片替换掉,这儿在提交的时候,就要注意一下,看代码:
handleAddSubmit = () => {
    const { form: { validateFields, setFieldsValue } } = this.props;
    const { fileList } = this.state;
    if (!fileList.length) {
      // 编辑图片时候,如果删掉缩略图,fileList会被清空,不过 getFieldsValue 获取file 的值还在,这时候提交无errors,但是fileList是空的
      setFieldsValue({ file: undefined });
    }
    validateFields((errors, values) => {
      if (!errors) {
        let data = {
          url: fileList[0].thumbUrl,
        }
       // 以下做你的骚操作。。。
     }
 }
图片1.png

以上代码有一句注释,说啥意思呢?意思就是,我们在编辑图片的时候,可能想把图片换一张,就删掉了,然而,这个时候打了一秒钟瞌睡,醒来就搞忘了,就直接点提交。这个时候呢,本来 Form 组件在使用 getFieldDecorator 时,已经设置必填了,然而图片这个时候虽然看似删掉了,因为以上示例图片处已经变成一个上传提示按钮了,但是提交事件虽然没有提示以上 errors错误,但是 thumbUrl 却报错 undefined 了?这特么就尴尬了。
这啥原因呢?原因就是 validateFields 的函数参数 的values 参数中,file 字段的值(就是被删掉的图片的地址)并没有随着点击示例图上删除那样,把 file 字段的值重置,删除图片的操作只是把 fileList 状态重置清空了。这个时候提交结果是,Form.Item 处并没有 必填的错误提示,validateFields 通过了必填验证,然而,fileList 早已经变成了空数组。this.props.form.getFieldValue('file') 也就是上一张图的路径却还在。
所以,在操作这种情况时,要判断 fileList 是否为空,如果为空表示图片已经被删除,那么同时将 file 的状态同步一下,做了这个操作后,validateFields 验证空图片的时候,就会有错误提示“请上传图片”。

  • 其三:这是个有点诡异的却又可能出现的,那就是,你可能已经以最规范的方式,操作了 Upload 组件,但是,onchange 事件中,e.file.status 死活都是 "uploading",硬不给你 "done",你说气人不?
    这个时候,你就要看看,你的组件的 DOM 是否 持续 render 了。为啥这么说呢?因为,最上面已经说了,你要保证每次 setState 后 fileList 的状态都是最新的,虽然你 Upload 操作很规范,但是,fileList 的变化没法让 DOM 节点及时渲染,这个数据流的过程因为你的 DOM 或操作过 React 生命周期等等因素,没法及时反映到 DOM 渲染上。比如你 shouldComponentUpdate 那儿逻辑是不是有问题?封装组件的时候,哪儿忘了 fileList 是个引用类型,没有深层比较,或者因为其他原因,组件根本就不能触发 render。
    收工!

你可能感兴趣的:(记一笔之:antd 的 Upload 上传组件 uploading 状态踩坑记)