React中单个useEffect获取的下拉数据如何跨组件复用无需刷新
问题根因
该现象通常由3个问题导致:
- 未处理Redux中
portfolios的初始状态,初始值为undefined/null时,下拉组件渲染阶段会抛出异常,阻塞后续数据返回后的重渲染流程 - 数据请求仅在固定组件挂载时触发一次,直接访问第二个页面对应路由时,组件渲染早于请求返回,且没有加载兜底逻辑
- 请求逻辑耦合在单个组件中,无法复用也无法做重复请求拦截
解决方案
1. 封装可复用的自定义请求Hook
将portfolios的查询逻辑封装为全局通用Hook,自带重复请求拦截,任意组件需要该数据时直接调用即可,只会在首次无数据时发起一次请求,后续统一复用Redux中缓存的数据:
// src/hooks/usePortfolios.js import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { setPortfolios } from '你的action文件路径'; export const usePortfolios = () => { const dispatch = useDispatch(); // 从Redux读取缓存数据、加载状态 const { portfolios, loading } = useSelector(state => ({ portfolios: state.portfolios, loading: state.portfoliosLoading })); useEffect(() => { // 已有数据/正在加载时跳过请求 if (portfolios.length > 0 || loading) return; const requestOptions = { method: 'GET', headers: { 'Content-Type': 'application/json' }, credentials: 'include', }; fetch('/portfolios', requestOptions) .then((response) => { if (response.status !== 200) throw new Error(response.status); return response.json(); }) .then((portfolios) => { dispatch(setPortfolios(portfolios)); }) .catch((error) => console.log(error)); }, [dispatch, portfolios, loading]); return { portfolios, loading }; };
2. 调整Redux初始状态与Reducer逻辑
给portfolios相关状态加初始值,避免渲染异常,同时新增加载状态控制:
// 对应reducer文件 const initialState = { portfolios: [], // 初始为空数组,避免渲染时取值报错 portfoliosLoading: false }; export default function portfolioReducer(state = initialState, action) { switch(action.type) { case 'portfolios:addPortfolios': return { ...state, portfolios: action.payload, portfoliosLoading: false }; default: return state; } }
3. 下拉组件复用Hook并添加加载兜底
两个下拉组件都可以直接调用该Hook获取数据,无需重复写请求逻辑,同时添加加载状态提示:
import { usePortfolios } from 'src/hooks/usePortfolios'; // 下拉组件通用逻辑 const PortfolioSelect = (props) => { const { portfolios, loading } = usePortfolios(); return ( <FormControl variant="outlined"> <Autocomplete onInputChange={(event, newInputValue) => { props.onUpdatePortfolio(newInputValue); }} id="futures-portfolio-select" options={portfolios} loading={loading} // 加载中时组件自动显示loading样式 getOptionLabel={(option) => option.portfolio_label} renderInput={(params) => <TextField {...params} label="Portfolio" variant="outlined" />} /> </FormControl> ); };
修改完成后,无论从哪个路由进入应用,都会自动触发数据请求,加载完成后所有复用该Hook的下拉组件都会自动更新,无需手动刷新页面,也不会重复发起请求。
内容的提问来源于stack exchange,提问作者user1285928
相关产品推荐
相关产品推荐

