Recoil无状态变更时重复触发API请求的问题解决
问题:Recoil状态未变更时路由切换仍触发API请求
近期使用Recoil的useRecoilStateLoadable调用API接口,整体运行正常,但在数据表格功能中遇到问题:希望仅在分页切换、搜索等状态实际变更时触发API请求,当前每次路由切换返回页面时,哪怕状态没有变化,API仍会被触发;若移除同步用的useEffect,路由切换时不会触发API,但分页、搜索操作也无法触发请求了。
现有Recoil原子与选择器代码
const entriesPerPage : EntriesPerPage = {defaultValue: 10}; export const filterScorecard = atom<ScorecardSearchRequest>({ key: 'filterScorecard', default: { Draw: 2, PageNumber: 1, PageSize: entriesPerPage.defaultValue!, SortColumnNames: [], SortDirections: [], SearchValue: '', ConsolidationValues: null, IsGroupDataManagement: false, OnlySearchLockedScorecards: null, ScorecardEndDate: null, ScorecardTypes: ["43"], Usernames: null, } }) export const allScorecardSelector = selector<ScorecardLicense>({ key: 'allScorecardSelector', get: async ({get}) => { const filterScorecardData = get(filterScorecard); console.log(filterScorecard) try{ const scorecardSearchResult = await BEEScorecardService.getUserScorecards(filterScorecardData); return scorecardSearchResult.data; }catch (error){ console.error(error); return defaultObject; } } }) export const allScorecardAtom = atom<ScorecardLicense>({ key: 'allScorecardAtom', default: allScorecardSelector })
组件内的useEffect同步代码
const [entriesPerPage, setEntriesPerPage] = useState<EntriesPerPage>({ defaultValue: 10 }); const [pageNumber, setPageNumber] = useState<number>(1); const [searchValue, setSearchValue] = useState<string>(''); const [sortColumnNames, setSortColumnNames] = useState<string[]>([]); const [sortDirections, setSortDirections] = useState<SortDirections[]>([]); useEffect(() => { setFilterScorecard(prevState => ({ ...prevState, pageNumber, entriesPerPage, searchValue, sortColumnNames, sortDirections })) }, [entriesPerPage, pageNumber, searchValue, sortColumnNames, sortDirections]);
解决思路与方案
核心问题在于组件本地状态与Recoil原子的冗余同步,以及Recoil选择器的触发逻辑:
1. 直接用Recoil原子管理筛选条件(推荐)
没必要同时维护组件useState和Recoil原子,直接将分页、搜索等状态统一放到filterScorecard原子中,组件通过useRecoilState直接操作,避免同步时的无意义更新:
// 组件内直接操作Recoil原子 const [filter, setFilter] = useRecoilState(filterScorecard); // 分页切换示例 const handleNextPage = () => { setFilter(prev => ({...prev, PageNumber: prev.PageNumber + 1})); }; // 搜索操作示例 const handleSearch = (value: string) => { setFilter(prev => ({...prev, SearchValue: value, PageNumber: 1})); };
Recoil的selector默认会缓存结果,只有当依赖的filterScorecard原子实际值发生变化时,才会重新执行API请求。
2. 保留本地状态时添加深比较逻辑
若因业务需求必须保留组件本地状态,需在useEffect中添加深比较,仅当本地状态与Recoil原子的对应值不一致时才更新原子:
import isEqual from 'lodash.isequal'; // 先获取当前Recoil原子的状态 const currentFilter = useRecoilValue(filterScorecard); useEffect(() => { // 构造新的筛选条件 const newFilterParams = { PageNumber: pageNumber, PageSize: entriesPerPage.defaultValue, SearchValue: searchValue, SortColumnNames: sortColumnNames, SortDirections: sortDirections }; // 对比当前原子状态与新参数,仅变化时更新 if (!isEqual(newFilterParams, { PageNumber: currentFilter.PageNumber, PageSize: currentFilter.PageSize, SearchValue: currentFilter.SearchValue, SortColumnNames: currentFilter.SortColumnNames, SortDirections: currentFilter.SortDirections })) { setFilterScorecard(prev => ({...prev, ...newFilterParams})); } }, [entriesPerPage, pageNumber, searchValue, sortColumnNames, sortDirections, currentFilter]);
内容的提问来源于stack exchange,提问作者Mohammed
相关产品推荐
相关产品推荐

