Closed reggieofarrell closed 4 years ago
Reselect isn't doing anything - your code is mutating the existing data:
breakouts.participantRoomAssignments.forEach(rmAss => {
if (!breakoutRooms[rmAss.roomId].participants) {
breakoutRooms[rmAss.roomId].participants = [];
}
breakoutRooms[rmAss.roomId].participants.push(rmAss.participantId);
});
Those objects are still the original reference, not copies.
Don't do that :) You'll probably want to make copies here and return those instead.
Thanks for the quick reply @markerikson. I actually assumed that was the case initially, but I tried this and I get the same results...
export const getRoomMap = createSelector(
[breakoutsSessionSelector],
(breakouts) => {
const breakoutsCopy = {...breakouts};
if (!breakoutsCopy?.participantRoomAssignments.length) return null;
const breakoutRooms = keyBy(breakoutsCopy.rooms, 'id');
breakoutsCopy.participantRoomAssignments.forEach(rmAss => {
if (!breakoutRooms[rmAss.roomId].participants) {
breakoutRooms[rmAss.roomId].participants = [];
}
breakoutRooms[rmAss.roomId].participants.push(rmAss.participantId);
});
return breakoutRooms;
}
);
...which is why I was scratching my head. But I just remembered that you can't deep clone objects with the spread operator 🤦🏻♂️. lodash's cloneDeep
to the rescue. It's working properly now. Thanks! Working code...
import { keyBy, cloneDeep } from 'lodash';
export const breakoutsSessionSelector = (state) => state.firestore.data.breakouts || {};
export const getRoomMap = createSelector(
[breakoutsSessionSelector],
(breakouts) => {
const breakoutsCopy = cloneDeep(breakouts);
if (!breakoutsCopy?.participantRoomAssignments.length) return null;
const breakoutRooms = keyBy(breakoutsCopy.rooms, 'id');
breakoutsCopy.participantRoomAssignments.forEach(rmAss => {
if (!breakoutRooms[rmAss.roomId].participants) {
breakoutRooms[rmAss.roomId].participants = [];
}
breakoutRooms[rmAss.roomId].participants.push(rmAss.participantId);
});
return breakoutRooms;
}
);
Yep, the spread operator is a shallow clone, which is a common mistake:
You might want to consider using https://immerjs.github.io/immer/docs/introduction to let you write "mutating" logic that get safely turned into immutable updates.
Note that we also use Immer internally in our official Redux Toolkit package:
I'm using reselect with redux and react (with hooks). Having some strange behavior where a selector I'm creating with
createSelector
is actually modifying my redux state.reselect: 4.0.0 redux: 4.0.5 react-redux: 7.0.2 react: 16.13.1
This code is in a file outside of the react component...
The data structure of
state.firestore.data.breakouts
in redux is...state.firestore.data.breakouts
stores data about what room meeting participants are in. ThegetRoomMap
selector's purpose is to create a map of the breakout rooms with the participants in an array inside of the rooms like so...This works just fine. The odd thing that I can't figure out is why when I call
const roomMap = useSelector(getRoomMap)
inside one of my react components, it actually modifiesstate.firestore.data.breakouts
such that therooms
array also has the array of participant ids in it like so...Is this normal? It's not breaking anything, but it is very odd. Why is creating this selector modifying my redux state?