如何在请求收发分离的React类组件中使用React Query?
解决类组件中结合React Query与消息驱动请求的方案
完全可行,核心思路是把你的Engine消息驱动逻辑适配成React Query能识别的异步模式,或者直接手动利用React Query的缓存API来管理状态。以下是两种具体实现方案:
方案一:将请求封装为Promise适配React Query
React Query的查询函数要求返回Promise,我们可以把Engine的"发送消息+等待响应"逻辑封装成一个Promise,这样就能直接和React Query的fetchQuery结合使用。
1. 封装Engine请求为Promise函数
function fetchWithEngine(requestParams) { return new Promise((resolve, reject) => { const msg = createAPIRequestMsg(requestParams); // 创建临时订阅者监听响应 const tempSubscriber = { receive(from, message) { if (message.id === msg.id) { // 收到对应响应后取消订阅 Engine.unsubscribe(tempSubscriber, 'APIResponse'); if (message.error) { reject(message.error); } else { resolve(message.response); } } } }; Engine.listenFor(tempSubscriber, 'APIResponse'); Engine.sendMessage(tempSubscriber, msg); }); }
2. 在类组件中使用React Query
通过withQueryClient高阶组件让类组件获取QueryClient实例,然后执行查询:
import { withQueryClient, QueryClientConsumer } from 'react-query'; class ComponentA extends React.Component { componentDidMount() { const { queryClient } = this.props; // 执行查询,React Query会自动管理加载、缓存、重复请求 queryClient.fetchQuery( ['apiData', /* 可加入请求参数作为缓存键的一部分,避免缓存冲突 */], () => fetchWithEngine({/* 请求参数 */}) ); } render() { return ( <QueryClientConsumer> {queryClient => { const data = queryClient.getQueryData(['apiData']); const isLoading = queryClient.isFetching(['apiData']); const error = queryClient.getQueryError(['apiData']); if (isLoading) return <div>加载中...</div>; if (error) return <div>请求失败:{error.message}</div>; return <div>{JSON.stringify(data)}</div>; }} </QueryClientConsumer> ); } } export default withQueryClient(ComponentA);
方案二:手动用QueryClient管理缓存状态
如果不想封装Promise,也可以直接在原有的fetchData和receive方法中操作React Query的缓存:
import { withQueryClient } from 'react-query'; class ComponentA extends React.Component { messageId; constructor(props){ super(props); Engine.listenFor(this, 'APIResponse'); } componentDidMount(){ this.fetchData(); } fetchData(){ const msg = createAPIRequestMsg({/* 请求参数 */}); this.messageId = msg.id; // 标记查询为加载状态 this.props.queryClient.setQueryData(['apiData'], { status: 'loading' }); Engine.send(msg); } receive(from, message){ if(message.id === this.messageId){ if(message.error){ // 更新缓存的错误状态 this.props.queryClient.setQueryError(['apiData'], message.error); } else { // 更新缓存数据 this.props.queryClient.setQueryData(['apiData'], message.response); } } } componentWillUnmount() { // 组件卸载时取消订阅,避免内存泄漏 Engine.unsubscribe(this, 'APIResponse'); } render() { const data = this.props.queryClient.getQueryData(['apiData']); const error = this.props.queryClient.getQueryError(['apiData']); const isLoading = data?.status === 'loading'; if (isLoading) return <div>加载中...</div>; if (error) return <div>请求失败:{error.message}</div>; return <div>{JSON.stringify(data)}</div>; } } export default withQueryClient(ComponentA);
关键注意事项
- 无论哪种方案,都要在组件卸载时取消Engine的订阅,防止内存泄漏
- 缓存键(如
['apiData'])建议加入请求参数,避免不同请求的缓存冲突 - 如果需要支持重新请求、缓存失效等React Query特性,方案一的Promise封装方式更友好
内容的提问来源于stack exchange,提问作者Louay Al-osh
相关产品推荐
相关产品推荐

