记一笔之: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: <span>{e.file.response.msg}</span>,
          okText: 'ok',
        });
      }
    }
    this.setState({ fileList: e.fileList });
  };

对应的react DOM结构可以是:

          <FormItem label="上传图片">
                {this.props.form.getFieldDecorator('file', { rules: [{ required: true, message: '请上传图片' }] })(
                    <div>
                      <Upload
                        name="file"
                        multiple={false}
                        headers={{ Accept: 'application/json', token: getToken() }}
                        action={this.state.imgUploadUrl}
                        listType="picture-card"
                        fileList={this.state.fileList}
                        onPreview={this.handlePreview}
                        onChange={this.handleChange}
                        accept="image/jpg, image/png"
                      >
                        {this.state.fileList.length >= 1 ? null : <div><Icon type="plus" /><div className="ant-upload-text">上传</div></div>}
                      </Upload>
                      <Modal visible={this.state.previewVisible} footer={null} onCancel={this.handleCancel}>
                        <img alt="example" style={{ width: '100%' }} src={this.state.previewImage} />
                      </Modal>
                    </div>
                )}
              </FormItem>

如上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。
    收工!
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容