为何tRPC的Context在WebSocket的每个Procedure调用时都会重置?
tRPC WebSocket连接下跨Procedure共享Context状态的问题解决
问题原因
tRPC使用wsLink时,虽然Context是在用户首次WebSocket连接时创建,但每次调用Procedure时,框架会对顶层Context对象做一次浅拷贝:
- 直接给Context的顶层属性赋值(比如
ctx.foo = 'bar'),只会修改本次调用的拷贝版Context,不会影响原始的连接级Context,下次调用时顶层属性会回到初始状态。 - 修改Context里嵌套对象的属性(比如
ctx.state.foo = 'bar')时,因为浅拷贝只复制对象引用,实际修改的是原始Context里的嵌套对象,状态就能在跨Procedure调用时保留。
解决方法
1. 用嵌套对象统一存储共享状态
初始化Context时就定义专门的嵌套状态容器,所有需要跨Procedure共享的状态都存放在这里:
// Context创建函数示例 const createContext = async ({ ws }) => { return { // 嵌套对象存储共享状态 sharedState: { activeRoom: null, userPreferences: {} }, // 顶层属性建议只放初始化后不修改的固定值 userId: ws?.userId }; };
在Procedure里修改嵌套属性:
const joinRoomProcedure = publicProcedure .input(z.string()) .mutation(async ({ ctx, input }) => { // 修改嵌套属性,状态会在同连接的其他Procedure中保留 ctx.sharedState.activeRoom = input; return { joined: true }; });
2. 禁止直接修改Context顶层属性
不要直接对Context的顶层属性做赋值操作,这类操作无法影响原始连接的Context。如果必须更新用户ID这类信息,也把它放到嵌套的状态对象里。
3. 确认WebSocket连接的生命周期
可以在createContext里添加日志,确认它只在用户首次连接时执行,而非每次调用Procedure都触发——如果每次调用都触发,说明WebSocket连接没有保持长连接,需要先排查连接配置问题。
内容的提问来源于stack exchange,提问作者Ben Butterworth
相关产品推荐
相关产品推荐

