React中使用Axios请求后端后数组缺失元素问题排查
问题描述
使用ReactJS结合Axios从后端获取数据,经Postman验证后端应返回包含5个元素的数组,但出现以下异常:
- 控制台打印
response.data.data时,展开前显示为5个元素的数组,展开后只剩4个 - 单独打印数组索引0到4的元素时,能获取到“缺失”的那个元素,同时元素顺序存在异常
- 无法确定是否为Axios的Bug
相关代码
获取消息的API函数
export const getRoomMessages = async (chatRoomId, limit = 20, page = 1) => { try { const response = await backendApi.get( `/chat_rooms/${chatRoomId}/chat_room_messages?limit=${limit}&page=${page}`, ); console.log('reponse', response.data.data); console.log('data0', response.data.data[0]); console.log('data1', response.data.data[1]); console.log('data2', response.data.data[2]); console.log('data3', response.data.data[3]); console.log('data4', response.data.data[4]); return [null, response.data]; } catch (e) { return [e, null]; } };
组件状态更新代码
componentDidMount() { this.initialFetch(); this.connectToWebSocket(); } componentWillUnmount() { this.disconnectFromWebsocket(); } initialFetch = async () => { this.fetchRoomDetail(); this.fetchMessages(); }; fetchMessages = async () => { this.setState({ loading: true, error: null }); const [err, res] = await getRoomMessages( this.roomId, this.state.pagination.size, 1, ); if (res) { this.setState({ messages: res.data.reverse(), pagination: { ...this.state.pagination, current: 2, }, noMoreLoad: res.paging.total_page <= 1 ? true : false, loading: false, }); this.scrollToBottom(); await this.readMessage(res.data.pop().id); } else { this.setState({ error: errorToString(err), loading: false }); } };
问题原因
- 控制台显示异常:浏览器控制台打印数组/对象时采用的是引用式快照——打印瞬间显示的是元素数量的即时值,但展开时会读取内存中数组的最新状态。你看到展开后少一个元素,是因为后续代码修改了这个数组。
- 数组被修改的根源:
fetchMessages中调用了res.data.pop(),pop()是会直接修改原数组的方法,会删除数组最后一个元素;同时res.data.reverse()同样会直接反转原数组,这两个操作都改变了response.data.data的原始引用,导致控制台展开时数组状态已变化。 - 顺序异常:
reverse()修改原数组的操作,直接改变了数组元素的原始顺序,所以单独打印索引时会出现顺序和预期不符的情况。
解决方法
核心是避免直接修改原数组,通过创建数组副本来进行操作:
修改后的fetchMessages代码
fetchMessages = async () => { this.setState({ loading: true, error: null }); const [err, res] = await getRoomMessages( this.roomId, this.state.pagination.size, 1, ); if (res) { // 创建数组副本再反转,不修改原数组 const reversedMessages = [...res.data].reverse(); this.setState({ messages: reversedMessages, pagination: { ...this.state.pagination, current: 2, }, noMoreLoad: res.paging.total_page <= 1 ? true : false, loading: false, }); this.scrollToBottom(); // 直接读取最后一个元素ID,不删除原数组元素 await this.readMessage(res.data[res.data.length - 1].id); } else { this.setState({ error: errorToString(err), loading: false }); } };
额外建议
如果需要对返回的数组进行多次操作,建议先创建完整副本:const messagesCopy = [...res.data],后续所有操作都基于这个副本,彻底避免意外修改原数据导致的异常。
内容的提问来源于stack exchange,提问作者Kevin Jayden Wivano
相关产品推荐
相关产品推荐

