ReactJS中Save按钮首次无响应、二次触发的原因排查
问题:首次点击Save按钮无反应,第二次才触发逻辑
我有两个函数:uploadFile负责将图片上传至Firebase并通过setState存储返回的URL;saveTeam向/new-team-member接口发送请求,请求载荷包含该URL。相关代码如下:
const uploadFile = async () => { const imageRef = storageRef(storage, `images/${UniqueComponentId()}`); uploadBytes(imageRef, team.image) .then((snapshot) => { getDownloadURL(snapshot.ref) .then((url) => { setTeam({ ...team, url: url || team.image }); }) .catch((error) => { console.log(error.message); }); }) .catch((error) => { console.log(error.message); }); }; const saveTeam = async () => { setSubmitted(true); let _teams = [...teams]; let _team = { ...team }; if (team.id) { await fetch('https://bunaq-api.vercel.app/api/edit-team-member', { method: 'PUT', body: JSON.stringify(team), headers: { 'Content-Type': 'application/json' }, }); const index = findIndexById(team.id); _teams[index] = _team; toast.current.show({ severity: 'success', summary: 'Successful', detail: t('adminTeamPage.new.toastUpdated'), life: 5000, }); } else { fetch('https://bunaq-api.vercel.app/api/new-team-member', { method: 'POST', body: JSON.stringify(team), headers: { 'Content-Type': 'application/json' }, }); _teams.unshift(_team); // location.reload(); toast.current.show({ severity: 'success', summary: 'Successful', detail: t('adminTeamPage.new.toastCreated'), life: 5000, }); } setTeams(_teams); setTeamDialog(false); setTeam(emptyTeam); }; const saveTeamMain = async () => { await uploadFile(); saveTeam(); }; <button onClick={saveTeamMain}>Save</button>;
点击Save按钮时,首次点击无任何反应,第二次点击才会触发函数逻辑,请问原因是什么?
核心原因
uploadFile未正确等待异步操作:
虽然uploadFile声明为async,但内部使用.then()链式调用时没有返回Promise,导致await uploadFile()会立即执行完毕。saveTeam在图片上传完成、URL存入team状态前就已经执行,此时发送的接口请求用的是旧的team数据,没有包含新的图片URL。第一次点击后的状态更新要等组件重新渲染才生效,第二次点击时才能拿到更新后的team,看起来像是第二次才触发逻辑。React状态更新的异步性:
setTeam是异步批量更新的,即使uploadFile等待完成,saveTeam也无法立刻拿到刚更新的team状态,因为组件状态不会同步修改。
修复方案
1. 让uploadFile返回Promise并返回更新后的team数据
修改uploadFile为async/await写法,确保异步操作完成后返回最新的team对象:
const uploadFile = async () => { try { const imageRef = storageRef(storage, `images/${UniqueComponentId()}`); const snapshot = await uploadBytes(imageRef, team.image); const url = await getDownloadURL(snapshot.ref); // 返回包含新URL的team对象 return { ...team, url: url || team.image }; } catch (error) { console.log(error.message); // 上传失败时返回原team,不中断后续流程 return team; } };
2. 修改saveTeamMain,传递最新team数据给saveTeam
const saveTeamMain = async () => { const updatedTeam = await uploadFile(); saveTeam(updatedTeam); };
3. 调整saveTeam,使用传入的最新数据而非组件状态
const saveTeam = async (updatedTeam) => { setSubmitted(true); let _teams = [...teams]; let _team = updatedTeam; if (_team.id) { await fetch('https://bunaq-api.vercel.app/api/edit-team-member', { method: 'PUT', body: JSON.stringify(_team), headers: { 'Content-Type': 'application/json' }, }); const index = findIndexById(_team.id); _teams[index] = _team; toast.current.show({ severity: 'success', summary: 'Successful', detail: t('adminTeamPage.new.toastUpdated'), life: 5000, }); } else { // 给POST请求加上await,确保请求完成后再更新状态 await fetch('https://bunaq-api.vercel.app/api/new-team-member', { method: 'POST', body: JSON.stringify(_team), headers: { 'Content-Type': 'application/json' }, }); _teams.unshift(_team); toast.current.show({ severity: 'success', summary: 'Successful', detail: t('adminTeamPage.new.toastCreated'), life: 5000, }); } setTeams(_teams); setTeamDialog(false); setTeam(emptyTeam); };
内容的提问来源于stack exchange,提问作者johnny shepherd
相关产品推荐
相关产品推荐

