Search code examples
angularreduxngrx

How can I get NGRX/Redux to stop transmitting data to subscribers when the data in the store hasn't changed?


When the user completes a test in MatchingPairsComponent, the following function is called to transmit the user's result to the backend.

private updateUserTestRecord = (testType) => {
const newTestRecord = {
    topicName: this.topicName,
    subtopicName:this.subtopicName,
    rightAnswers: 'n/a',
    wrongAnswers: this.wrongMatches + ' of ' + this.matchingPairsLength,
    testType,
    date: new Date(),            
}

  this.userService.updateUserTestRecord(newTestRecord)
 }

The UserService function called above to transmit the user data to the backend is shown here:

public updateUserTestRecord = (newTestRecord) => {
this.httpClient
  .put<{ testRecords }>(`${this.url}/update/user/record`, newTestRecord).pipe(
    tap(testRecords => {
      console.log('testRecords',  testRecords)
      this.store.dispatch(userActions.SaveTestRecords({ testRecords }));
    }),
    catchError((errorObj) => {
      this.store.dispatch(
        messageActions.SetError({ error: errorObj.error })
      );
      throw errorObj;
    })
  ).subscribe();
 }

The above function receives the updated test record from the backend and updates the UserReducer. The UserReducer function is shown here:

on(userActions.SaveTestRecords, (state, action) => {
console.log('SaveTestRecords.testRecords', action.testRecords)
const currentState =  {
    ...state,
    testRecords: action.testRecords     
}

saveStateToLocalStorage(currentState);

return currentState;
 }),

These all work as expected. The problem is when UserService updates UserReducer, data is also transmitted from the TopicsReducer to MatchingPairsComponent which subscribes to TopicsReducer even though no data in TopicsReducer changes.

To repeat, UserReducer is updated, but data is transmitted from TopicReducer to MatchingPairsComponent even though TopicReducer was never updated. Here is the TopicReducer function that MatchingPairsComponent subscribes to:

export const TopicsReducers = createReducer(initialState,
on(topicsActions.StoreSubtopicData, (state, action) => { 
  console.log('Storetopics.topics', action.subtopicData)   
  return {
    ...state,
    subtopicData: action.subtopicData,
  };
}),)

And here is the MatchingPairsComponent's subscription to TopicsReducer:

ngOnInit() {
   this.subscribeToReduxStores();     
 }

 private subscribeToReduxStores = () => {
window.speechSynthesis.cancel();

const topicQuestions$ = this.store.select((state) => {      
  console.log('state.topicsState', state.topicsState)

  return {
    matchingPairs: state.topicsState.matchingPairs, 
    subtopicName: state.topicsState.subtopicData.subtopicName,
    topicName: state.topicsState.subtopicData.topicName,        
  }
})

topicQuestions$.subscribe(data => {    
  console.log('topicQuestions.subscribe', data); 
  this.matchingPairs = JSON.parse(JSON.stringify(data.matchingPairs));   
  this.matchingPairsLength = this.matchingPairs.length;   
  this.topicName = data.topicName; 
  this.subtopicName = data.subtopicName;       
  this.numberOfSegments = Math.ceil(this.matchingPairs.length/this.MAXPAIRS);
  this.wrongMatches = 0;
  console.log('matchingPairs', this.matchingPairs)
  this.setupPairs(); 
})

}

Why is TopicsReducer transmitting data to MatchingPairsComponent, overwriting data that is currently being processed in MatchingPairsComponent when the data in TopicsReducer has not changed?


Solution

  • Changed the select statement of MatchingPairsComponent subscription to redux to only return 'state.topicsState'.

    const topicQuestions$ = this.store.select((state) => {      
      console.log('state.topicsState', state.topicsState)
    
      // return {
      //   // matchingPairs: state.topicsState.matchingPairs, 
      //   // subtopicName: state.topicsState.subtopicData.subtopicName,
      //   // topicName: state.topicsState.subtopicData.topicName,        
      // }
    
      return state.topicsState
    })
    

    Changed the subscribe statement to use topicsState accordingly:

    topicQuestions$.subscribe(topicsState => {    
      console.log('topicQuestions.subscribe', topicsState); 
      this.matchingPairs = JSON.parse(JSON.stringify(topicsState.matchingPairs));   
      this.matchingPairsLength = this.matchingPairs.length;   
      this.topicName = topicsState.subtopicData.topicName; 
      this.subtopicName = topicsState.subtopicData.subtopicName;       
      this.numberOfSegments = Math.ceil(this.matchingPairs.length/this.MAXPAIRS);
      this.wrongMatches = 0;
      console.log('matchingPairs', this.matchingPairs)
      this.setupPairs(); 
    })