React多异步API调用场景下使用状态标志位的合理性及列表问题咨询
项目开发背景
- 技术栈:采用React开发前端项目,使用Redux进行状态管理,目前已封装可复用的通用
fetchAPIcallaction做请求逻辑复用。 - 业务场景规划:初始加载阶段需要发起3个独立API请求,后续会拓展物品新增、编辑等场景的更多API调用逻辑。
- 对接接口范围:覆盖获取用户信息、收藏列表增/删/查、物品列表增/改/删/查,后端为自研Rails实现的API服务。
- 代码托管:项目公开仓库为find-my-item,基于适配Node 12的功能分支进行开发。
当前遇到的问题
- 为保障API调用执行顺序符合预期,尝试在组件内维护
apiFlag状态实现流程控制。 - 业务场景存在大量异步操作,其中包括用户通过点赞按钮快速添加/移除收藏物品这类高频交互。
- 突出故障表现:收藏列表更新不及时,需要重新跳转页面或退出重新登录才能看到数据变更;原本设计用来在收藏操作后复用刷新逻辑的
GetItemsNFavlist组件,目前刷新逻辑失效。
核心疑问
- 在React项目中同时处理大量异步函数/API调用时,使用组件状态标志位做流程控制是否为通用最佳实践?
- 如何在控制请求顺序、保证数据实时性的同时,减少冗余代码量?
现有实现代码
GetItemsNFavlist组件
该组件用于加载物品列表与对应用户的收藏列表,设计初衷是在收藏操作后复用该逻辑刷新收藏列表。组件内调用通用的fetchAPIcall action,通过Redux store中的fetchCall对象追踪请求状态与响应数据,当前在该组件中串行发起两个请求:获取全量物品列表、获取当前用户的收藏列表,具体实现代码如下:
import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import PropTypes from 'prop-types'; import * as MyActions from '../actions'; const GetItemsNFavlist = props => { const { actions, items, fetchCall, favList, user, } = props; const [apiFlag, setApiFlag] = useState({ itm: false, fvl: false }); const itemsUrl = 'https://findmyitem-api.herokuapp.com/items'; const favListUrl = `https://findmyitem-api.herokuapp.com/users/${user.id}/favorites_lists`; useEffect(() => { if (!apiFlag.itm && !apiFlag.fvl) actions.fetchAPIcall(itemsUrl, 'get', {}); }, []); useEffect(() => { if (!fetchCall.apiData && items[0]) { actions.fetchAPIcall(favListUrl, 'get', {}); setApiFlag({ itm: true, fvl: false }); } }, [items]); useEffect(() => { if (fetchCall.apiData && !items[0] && !favList[0]) { actions.setItems(fetchCall.apiData); actions.fetchAPIreset(); } if (apiFlag.itm && fetchCall.apiData && !favList[0]) actions.setFavList(fetchCall.apiData); }); useEffect(() => { if (favList[0]) { actions.fetchAPIreset(); setApiFlag({ itm: true, fvl: true }); } }, [favList]); return (<> </>); }; GetItemsNFavlist.propTypes = { user: PropTypes.objectOf(PropTypes.any).isRequired, actions: PropTypes.objectOf(PropTypes.any).isRequired, items: PropTypes.arrayOf(PropTypes.any).isRequired, favList: PropTypes.arrayOf(PropTypes.any).isRequired, fetchCall: PropTypes.objectOf(PropTypes.any).isRequired, }; const mapStateToProps = ({ user, items, fetchCall, favList, }) => ({ user, items, fetchCall, favList, }); function mapActionsToProps(dispatch) { return { actions: bindActionCreators({ ...MyActions }, dispatch), }; } export default connect(mapStateToProps, mapActionsToProps)(GetItemsNFavlist);
通用API action实现(actions/index.js)
核心为可复用的fetchAPIcall请求逻辑,具体实现代码如下:
import axios from 'axios'; const addUsername = username => ({ type: 'SET_NAME', username, }); const setUserInfo = user => ({ type: 'SET_USER', user, }); const setItems = items => ({ type: 'SET_ITEMS', items, }); const setFavList = favList => ({ type: 'SET_FAVLIST', favList, }); const fetchAPIbegin = callHeader => ({ type: 'FETCH_API_BEGIN', callHeader, }); const fetchAPIsuccess = payload => ({ type: 'FETCH_API_SUCCESS', payload, }); const fetchAPIfailure = error => ({ type: 'FETCH_API_FAILURE', payload: error, }); const fetchAPIsuccesResp = payload => ({ type: 'FETCH_API_SUCCESS_RESP', payload, }); function handleErrors(response) { if (!response.ok && response.error) { throw Error(JSON.stringify(response)); } return response; } function fetchAPIcall(url, restAct, options) { return dispatch => { dispatch(fetchAPIbegin(url, options)); setTimeout(() => axios[restAct](url, options) .then(handleErrors) .then(rsp => { dispatch(fetchAPIsuccesResp(rsp)); return rsp; }) .then(resp => resp.data) .then(jsonResp => dispatch(fetchAPIsuccess(jsonResp))) .catch(err => dispatch(fetchAPIfailure(`${err}`))), 1000); }; } const fetchAPIreset = () => ({ type: 'FETCH_API_RESET' }); export { addUsername, setUserInfo, setItems, setFavList, fetchAPIcall, fetchAPIbegin, fetchAPIsuccess, fetchAPIfailure, fetchAPIreset, fetchAPIsuccesResp, };
内容的提问来源于stack exchange,提问作者Alfred C.
相关产品推荐
相关产品推荐

