NGRX中dispatch与select执行顺序异常问题求助
问题分析与解决方案
你的问题核心在于每次调用addPost()时,订阅getCurrentPostHeaderId的时机和Redux异步action的执行时序不匹配,再加上RxJS订阅的特性,导致了旧值先被输出:
- 当你调用
dispatch(new postHeaderActions.AddToPost(postHeader))时,如果这是一个需要和服务器交互的异步action(比如基于redux-thunk/saga实现),dispatch之后并不会立刻触发Add_Successreducer——此时服务器请求还在进行中,Redux state里的selectedPostId还是上一次的旧值。 - 紧接着你调用
this.store.select(...).subscribe(...),RxJS的store Observable流会立即同步发出当前state的最新值,也就是旧的selectedPostId。 - 等服务器请求完成,
Add_Successaction被触发,reducer更新selectedPostId为新ID,此时Observable流才会发出新值,这就是你看到第二次及之后先输出旧ID、再输出新ID的原因。 - 第一次运行时,初始
selectedPostId是undefined,所以第一次订阅会先输出undefined(你可能没注意到),然后才输出新ID,看起来像是“正常”。
另外还有一个隐藏问题:每次点击按钮都会创建一个新的订阅,却没有取消订阅,这会导致内存泄漏,随着点击次数增加,订阅会越来越多,每次state更新都会触发所有旧订阅的回调。
解决方案1:利用异步action的返回值,等待action完成后再获取最新ID
如果你的AddToPost action是基于redux-thunk实现的,并且返回了一个Promise(比如在thunk里处理服务器请求,成功后resolve),那么可以等待Promise完成后再去获取最新的ID,同时用take(1)确保只取一次值并自动取消订阅:
addPost() { const postStatus = Object.assign({}, this.newPostForm.value); const postHeader: any = { postStatus }; // 假设AddToPost action返回Promise this.store.dispatch(new postHeaderActions.AddToPost(postHeader)) .then(() => { this.store.select(fromPost.getCurrentPostHeaderId) .pipe(take(1)) // 只取一次值,自动完成订阅 .subscribe(postId => { if (postId) { console.log(postId); // 这里只会拿到新的ID } }); }); }
解决方案2:在订阅中过滤旧值,只处理新增后的ID
如果无法通过Promise等待action完成,可以用RxJS的操作符过滤掉初始的旧值,比如记录点击前的ID,只处理不等于该值的新结果:
addPost() { const postStatus = Object.assign({}, this.newPostForm.value); const postHeader: any = { postStatus }; // 记录点击前的当前ID const preClickId = this.store.select(fromPost.getCurrentPostHeaderId).value; this.store.dispatch(new postHeaderActions.AddToPost(postHeader)); this.store.select(fromPost.getCurrentPostHeaderId) .pipe( filter(id => id !== preClickId), // 只处理和点击前不同的ID take(1) // 取到新ID后自动取消订阅 ) .subscribe(postId => { if (postId) { console.log(postId); } }); }
解决方案3:组件全局订阅,而非每次点击都订阅
更优雅的方式是在组件初始化时就订阅selectedPostId的变化,而不是每次点击按钮才订阅。这样既避免了重复订阅的内存泄漏,也能自然响应每次state的更新:
// 组件初始化时(比如Angular的ngOnInit) private selectedPostIdSubscription!: Subscription; ngOnInit() { this.selectedPostIdSubscription = this.store.select(fromPost.getCurrentPostHeaderId) .subscribe(postId => { if (postId) { // 这里可以处理所有ID变化的逻辑,包括新增后的ID console.log(postId); } }); } // 组件销毁时取消订阅,避免内存泄漏 ngOnDestroy() { this.selectedPostIdSubscription.unsubscribe(); } addPost() { const postStatus = Object.assign({}, this.newPostForm.value); const postHeader: any = { postStatus }; // 只需要dispatch action,订阅会自动处理后续的ID更新 this.store.dispatch(new postHeaderActions.AddToPost(postHeader)); }
额外建议
检查你的AddToPost action实现,确保它确实是在服务器请求成功后才触发Add_Success action,并且action.payload.id是正确的服务器返回的新帖子ID。可以在Add_Success reducer里打印action.payload.id,确认它每次都是新的唯一值。
内容的提问来源于stack exchange,提问作者scholarwithfire
相关产品推荐
相关产品推荐

