1

I need to data from one array to another in my Context state and I chose to do so using useReducer(). This is how that action looks

        case 'ADD_CATEGORY':{
            if(state.catList.length>0){
                let nextState = { ...state }; 
                let catList = state.catList.filter(c => c.localeCompare(state.category)!==0);
                let categories = state.categories;
                categories.push (state.category);

                nextState.categories = categories;
                nextState.catList = catList;
                console.log(state.category);
                return nextState
            }else return state;
        }

This is how the state looks

    const initialState = {
        towns:[],
        name: '',
        parish: '',
        category: '',
        townsToSubmit: [],
        categories:[],
        catList:[],
    }

According to this github issue I'm supposed to expect useReducer() to call twice if I'm reading the thread correctly since the react app is using strict mode and its to see if my code causes side effects. How do I do that when adding to an array.

The main issue that this causes is that my arrays end up having elements repeated because they're always added twice. Since StrictMode is supposed to help detect side effects is their a better way of adding data to an array?

Deeswoc
  • 159
  • 1
  • 13
  • Probably the double invocation is not due to the above issue but due to the fact that you might have used React.strictMode in your app. Please check this post: https://stackoverflow.com/questions/61578158/why-does-usestate-cause-the-component-to-render-twice-on-each-update/61578206#61578206 – Shubham Khatri May 20 '20 at 13:01
  • I actually made a mention of that in a recent edit. – Deeswoc May 20 '20 at 13:02
  • StrictMode only invokes the function twice in development mode. You can avoid this by removeing React.StrictMode from your componenthierarchy – Shubham Khatri May 20 '20 at 13:03
  • where does `state.category` come from? shouldn't that be more like `action.payload`? Can you show the entire function. – Thomas May 20 '20 at 13:17
  • @ShubhamKhatri The solution here is to fix the code so that it doesn't have side effects, not to turn off strict mode. – JLRishe May 20 '20 at 13:32
  • @JLRishe I agree with you and your solution is great – Shubham Khatri May 20 '20 at 13:42
  • Like @JLRishe says, you need to avoid mutating your arrays/objects. I recommend [Immer.js](https://immerjs.github.io/immer/docs/introduction#quick-example) to make it easier to use familiar methods like `.push` while keeping your reducer pure. – joshwilsonvu May 20 '20 at 13:42

1 Answers1

1

As the creators of Redux stress over and over, your reducers need to be pure. { ...state } creates a shallow copy of state, so if you modify the values of any reference types in the state (e.g. by using .push() on an array), that change will happen in both the previous version of the state and the next. If the same modification is repeated twice on account of strict mode, then this means you have a problem.

So use only pure operations:

 case 'ADD_CATEGORY':{
     if(state.catList.length > 0) {
         return {
            ...state,
            catList: state.catList.filter(c => c.localeCompare(state.category) !== 0),
            categories: [...state.categories, state.category],
         };
      }

      return state;
}
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • So does the state get set only once? I was under the impression that if I added a value to the array then the state would be changes to that and then it would be added on the second render cause by strict mode. Since the mutation was the issue would it correct to say that as long as nothing changes outside the function then the state will be updated only once? – Deeswoc May 20 '20 at 13:56
  • 1
    @Deeswoc Strict mode makes it so that the reducer is _called_ twice, but the previous->next state transition happens only once. This is to ensure that the reducer doesn't have side effects. – JLRishe May 20 '20 at 14:02