如何在不使用Redux的React中缓存已获取的数据
嘿,这个场景我太熟了!正好给你掰扯清楚Redux和React本地状态在路由切换时的核心差异,以及不用Redux该怎么解决~
当你不使用Redux,把视频数据存在VideosPage组件的本地状态(比如useState或者useReducer)里时,路由切换离开再返回,组件会重新挂载,本地状态会被完全重置——也就是说之前辛辛苦苦获取的视频数据全没了,得重新发起请求加载,用户体验瞬间拉胯:要么等加载转圈圈,要么看到空白页面,而不是像用Redux那样直接展示之前的内容。
两者核心差异对比
- Redux公共Store:
- 全局单例模式,数据完全独立于组件生命周期存在,只要你的应用没刷新,store里
videosreducer的数据就会一直保留 - 切换路由时
VideosPage组件卸载,但store里的数据半分不受影响,返回页面时直接读取已有数据,还能按需加载新数据追加进去
- 全局单例模式,数据完全独立于组件生命周期存在,只要你的应用没刷新,store里
- 组件本地状态:
- 状态和组件实例深度绑定,组件一旦卸载(比如路由跳走),本地状态就会被销毁清空
- 再次进入
/videos页面,组件重新初始化,状态回到初始值,必须重新发起请求拉取数据
不使用Redux时的替代方案
如果不想引入Redux,但又想保留页面数据提升用户体验,这几个办法亲测好用:
React Context + useReducer 自制全局状态:
原理和Redux类似,自己封装一个全局的状态容器,把视频数据存在Context里,让VideosPage组件从Context读取和更新数据,这样数据就脱离了组件生命周期的限制。举个简单的例子:// VideoContext.js import { createContext, useContext, useReducer } from 'react'; const VideoContext = createContext(); const videoReducer = (state, action) => { switch (action.type) { case 'APPEND_VIDEOS': return { ...state, videos: [...state.videos, ...action.payload] }; case 'CLEAR_VIDEOS': return { ...state, videos: [] }; default: return state; } }; export const VideoProvider = ({ children }) => { const [state, dispatch] = useReducer(videoReducer, { videos: [] }); return ( <VideoContext.Provider value={{ videos: state.videos, dispatch }}> {children} </VideoContext.Provider> ); }; export const useVideoContext = () => useContext(VideoContext);然后在
App.js里用VideoProvider包裹所有路由组件,VideosPage里就可以通过useVideoContext获取和更新视频数据啦~本地存储(localStorage/sessionStorage):
请求到视频数据后,把数据存在本地存储里,组件挂载时先读取本地存储的内容,再决定是否发起新请求。注意要处理数据过期的问题,比如给数据加个时间戳,超过一定时间就重新请求。状态提升到共同父组件:
如果VideosPage和其他页面都在同一个App或者Layout父组件下,把视频数据存在父组件的状态里,通过props传给VideosPage——这样路由切换时父组件不会卸载,状态就会一直保留。
内容的提问来源于stack exchange,提问作者Den Gas

