相信用 Upload 的很多老兄都遇到過(guò)這個(gè)問(wèn)題,就是在處理上傳文件時(shí)缀棍,從 onchange 方法中得到的 e.fileList 里面的 status 參數(shù)他么總是 uploading 熟嫩,而不是我們需要的 done 狀態(tài)秦踪,導(dǎo)致拿不到 thumbUrl 和 response 的 imgUrl 。
根據(jù)官網(wǎng)文檔上對(duì) 這個(gè)問(wèn)題 的解釋掸茅,就是要注意兩點(diǎn):
- 其一是要保證 onchange 事件內(nèi)部椅邓,至少有一次無(wú)阻礙的調(diào)用 setState 賦值 fileList,讓所有狀態(tài)變化都及時(shí)更新昧狮。當(dāng)然這個(gè)“無(wú)阻礙”是我自己的理解景馁,就是說(shuō) setState 的方法執(zhí)行最好不寫在 if 這類條件判斷里面,直接寫在最底部或者最頂部就行逗鸣;
- 其二是要保證每次 setState 后 fileList 的狀態(tài)都是最新的合住。所以,以上 issue 里面建議上一點(diǎn)我說(shuō)的寫法最好是:
this.setState({ fileList: [...this.state.fileList] })
不過(guò)對(duì)于以上第二點(diǎn)撒璧,個(gè)人親測(cè)透葛,即使直接 { fileList: e.fileList } 就可以。以下假定只傳一張圖片卿樱,一個(gè)完整的 onchange 事件內(nèi)部處理可以像下面一樣:
handleChange = e => {
if (e.file.status == 'done') {
if (e.file.response.code === 200) {
let result = e.fileList;
result[0].thumbUrl = result[0].response.imgUrl; //用絕對(duì)路徑替代base64僚害,有助于在保存圖片的時(shí)候避免字段過(guò)長(zhǎng)和減小數(shù)據(jù)庫(kù)的壓力
this.setState({ fileList: result });
} else {
Modal.warn({
title: '提示',
content: <span>{e.file.response.msg}</span>,
okText: 'ok',
});
}
}
this.setState({ fileList: e.fileList });
};
對(duì)應(yīng)的react DOM結(jié)構(gòu)可以是:
<FormItem label="上傳圖片">
{this.props.form.getFieldDecorator('file', { rules: [{ required: true, message: '請(qǐng)上傳圖片' }] })(
<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結(jié)構(gòu),在一些常規(guī)操作中繁调,上傳圖片的操作萨蚕,很可能會(huì)存在這樣的場(chǎng)景:新建記錄靶草、更新記錄、獲取記錄詳情门岔、保存記錄等等爱致,同時(shí)配合使用 Form 以及 Form.Item 組件,以及 getFieldValue寒随、setFieldValue糠悯、getFieldDecorator 等等 api 使 Upload 的操作受控。在使用 Upload 組件時(shí)妻往,也許需要注意以下幾點(diǎn):
- 其一:在獲取記錄詳情時(shí)互艾,拿到圖片的 url,一定要將 state 中的 fileList 的 status 狀態(tài)設(shè)為 done:
this.setState({
previewImage: url,
fileList: [ {
thumbUrl: url,
status: 'done', // 這個(gè)必須讯泣,不然纫普,圖片自然顯示不出來(lái)
uid: url, // 這個(gè)隨意吧
} ],
});
// 以及將受控字段的值設(shè)為 url:
this.props.form.setFieldsValue( { file: bannerUrl } )
- 其二:在編輯圖片的時(shí)候,比如想將圖片替換掉好渠,這兒在提交的時(shí)候昨稼,就要注意一下,看代碼:
handleAddSubmit = () => {
const { form: { validateFields, setFieldsValue } } = this.props;
const { fileList } = this.state;
if (!fileList.length) {
// 編輯圖片時(shí)候拳锚,如果刪掉縮略圖假栓,fileList會(huì)被清空,不過(guò) getFieldsValue 獲取file 的值還在霍掺,這時(shí)候提交無(wú)errors匾荆,但是fileList是空的
setFieldsValue({ file: undefined });
}
validateFields((errors, values) => {
if (!errors) {
let data = {
url: fileList[0].thumbUrl,
}
// 以下做你的騷操作。杆烁。牙丽。
}
}
以上代碼有一句注釋,說(shuō)啥意思呢兔魂?意思就是烤芦,我們?cè)诰庉媹D片的時(shí)候,可能想把圖片換一張析校,就刪掉了构罗,然而,這個(gè)時(shí)候打了一秒鐘瞌睡勺良,醒來(lái)就搞忘了,就直接點(diǎn)提交骄噪。這個(gè)時(shí)候呢尚困,本來(lái) Form 組件在使用 getFieldDecorator 時(shí),已經(jīng)設(shè)置必填了链蕊,然而圖片這個(gè)時(shí)候雖然看似刪掉了事甜,因?yàn)橐陨鲜纠龍D片處已經(jīng)變成一個(gè)上傳提示按鈕了谬泌,但是提交事件雖然沒(méi)有提示以上 errors錯(cuò)誤,但是 thumbUrl 卻報(bào)錯(cuò) undefined 了逻谦?這特么就尷尬了掌实。
這啥原因呢?原因就是 validateFields 的函數(shù)參數(shù) 的values 參數(shù)中邦马,file 字段的值(就是被刪掉的圖片的地址)并沒(méi)有隨著點(diǎn)擊示例圖上刪除那樣贱鼻,把 file 字段的值重置,刪除圖片的操作只是把 fileList 狀態(tài)重置清空了滋将。這個(gè)時(shí)候提交結(jié)果是邻悬,F(xiàn)orm.Item 處并沒(méi)有 必填的錯(cuò)誤提示,validateFields 通過(guò)了必填驗(yàn)證随闽,然而父丰,fileList 早已經(jīng)變成了空數(shù)組。this.props.form.getFieldValue('file') 也就是上一張圖的路徑卻還在掘宪。
所以蛾扇,在操作這種情況時(shí),要判斷 fileList 是否為空魏滚,如果為空表示圖片已經(jīng)被刪除镀首,那么同時(shí)將 file 的狀態(tài)同步一下,做了這個(gè)操作后栏赴,validateFields 驗(yàn)證空?qǐng)D片的時(shí)候蘑斧,就會(huì)有錯(cuò)誤提示“請(qǐng)上傳圖片”。
- 其三:這是個(gè)有點(diǎn)詭異的卻又可能出現(xiàn)的须眷,那就是竖瘾,你可能已經(jīng)以最規(guī)范的方式,操作了 Upload 組件花颗,但是捕传,onchange 事件中,e.file.status 死活都是 "uploading"扩劝,硬不給你 "done"庸论,你說(shuō)氣人不?
這個(gè)時(shí)候棒呛,你就要看看聂示,你的組件的 DOM 是否 持續(xù) render 了。為啥這么說(shuō)呢簇秒?因?yàn)橛愫恚钌厦嬉呀?jīng)說(shuō)了,你要保證每次 setState 后 fileList 的狀態(tài)都是最新的,雖然你 Upload 操作很規(guī)范扛禽,但是锋边,fileList 的變化沒(méi)法讓 DOM 節(jié)點(diǎn)及時(shí)渲染,這個(gè)數(shù)據(jù)流的過(guò)程因?yàn)槟愕?DOM 或操作過(guò) React 生命周期等等因素编曼,沒(méi)法及時(shí)反映到 DOM 渲染上豆巨。比如你 shouldComponentUpdate 那兒邏輯是不是有問(wèn)題?封裝組件的時(shí)候掐场,哪兒忘了 fileList 是個(gè)引用類型往扔,沒(méi)有深層比較,或者因?yàn)槠渌蚩桃蓿M件根本就不能觸發(fā) render瓤球。
收工!