0

I am coding along with the Redux Essentials tutorial.

When I run notificationArray.map without using the return value, my state is updated per the map functions callbacks.

I.e. in allNotifiicationsRead, each notifications's read property is set to true in the app state. And in fetchNotifications, all each notification's isNew property is set to !read.

I would expect this notificationArray.map(...) call to not have any effect since, I am not using the return value, let alone editing the state with it such as something like notificationsAdapter.setMany(notificationArray).

Any clarification on how this is working would be appreciated.

Please let me know if I didn't include any relevant details about this app.

const notificationsSlice = createSlice({
    name: 'notifications',
    initialState: notificationsAdapter.getInitialState(),
    reducers: {
        allNotificationsRead(state, action) {
            const notificationArray = Object.values(state.entities)
            notificationArray.map(notification => (
                notification.read = true
            ))
        }
    },
    extraReducers(builder){
        builder.addCase(fetchNotifications.fulfilled, (state, action) => {
            notificationsAdapter.upsertMany(state, action.payload)
            const notificationArray = Object.values(state.entities)
            notificationArray.map(notification => (
                notification.isNew = !notification.read
            ))
        })
    }
})
  • You did not assign the `Array#map` product to anything. – SMAKSS Jun 01 '22 at 21:05
  • First of all you shouldn't be using `map()` without a return as that is simply a `forEach()` with additional overhead, but the reason it's mutating your state is that you are directly changing a nested object's properties. `map` doesn't clone the contents of the array it's called on in any way. To avoid the mutation you **should** be using the returned array from the `map()` and you should be cloning each object in the callback `const notificationArray = Object.values(state.entities).map(notification => ({...notification, isNew: !notification.read}));` – pilchard Jun 01 '22 at 21:06
  • If you don't want to *mutate* the array, you shouldn't have statements that mutate (like `notification.read = true`). The alternative is to copy, but then where do you want to store it -- since you made `notificationArray` a `const`? – trincot Jun 01 '22 at 21:08
  • see: [JavaScript: Difference between .forEach() and .map()](https://stackoverflow.com/questions/34426458/javascript-difference-between-foreach-and-map) and [How to mutate original array in Javascript .map() function?](https://stackoverflow.com/questions/46520833/how-to-mutate-original-array-in-javascript-map-function#:~:text=If%20you%20want%20to%20mutate,can%20use%20Array%23forEach%20function.&text=Array%23map%20creates%20a%20new,to%20assign%20the%20returned%20result.) – pilchard Jun 01 '22 at 21:11
  • @pilchard Thank you, you explained this behavior perfectly. I didn't recognize here that I was simply modifying the array by writing `.map(notification => (...))` instead of cloning each object with `.map(notification => ({...}))`. It seems like the former will always lead to modifying the original array (as well as not returning a modified array) and therefore an antipattern. Can you point me to a resource that explains exactly what the syntax being used in each is? I.e. `.map(notification => (...))` modifies the array whereas `.map(notification => ({...}))` clones each item. – aDevCalledAlex Jun 01 '22 at 22:12
  • It's simply [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals) in an object literal which creates a shallow clone of the object which is spread while allowing for properties to be added, or updated using standard object literal syntax. – pilchard Jun 01 '22 at 22:24
  • The part I was missing was the [syntax of parentheses when returning in JS](https://stackoverflow.com/questions/20824558/why-use-parentheses-when-returning-in-javascript). As well as the fact that my return value comes from a reassignment, which returns the value of the variable after reassignment. And as you pointed out, the Map callback mutates the array as a side effect (which I know is an antipattern). Everything is crystal clear now. Thanks again! – aDevCalledAlex Jun 02 '22 at 15:29

0 Answers0