We have a react table component where we need the selections to work across different pages. So I have a Map to store those selected rows across the pages. But for manipulating the selections, I have a class called Selections
class Selections {
eventMap = new Map();
addSelections(event) {
....
return this;
}
}
In the react state, I instantiate this object and update the state when events are added or removed.
// constructor
this.state = {
selections: new Selections()
}
function onToggle(e, data) {
const { prevSel } = this.state;
this.setState({
selections: prevSel.toggle(data)
})
}
This is functioning as expected. However, another team member suggested it's a code smell and we should not be using custom class objects in selections because it encapsulates the methods as well. I'm not sure I fully understand why an instance of a custom class with methods is a bad thing. For me, the ability to reuse Selections in multiple table components across the product and unit test it separately outweighs the concern of having methods in the react state. Is there anything issue with this implementation I am missing? Is this not widely used in the industry?
Complex objects, such as a Map or a Set, will not trigger state updates as you change them, in much the same way an Object in state would not either. This is because listeners (useEffect, useMemo, whatever) that list those state variables in their dependency list or component props will always see the same object. With a standard object, you can put a specific object key as the reference in your dependency list, as long as the value of that key is a simple variable.
For your state changes to fire down the chain, every 'listener' for that state change would need a new value for the variable. So, if you are storing selections in state, and selections is an instance of Selections, then every state update would require a new instance of Selections for those updates to fire down the chain. If you just make a change to an existing Selections instance (even in state) and apply it to state, it is still the same object reference, which wouldn't trigger updates down the chain. You see the same with any complex object (Object, Array, etc).
By The Way, I do something very similar, by storing selections in a Set (I only store an object's primary key) in state. When I need to update state, it goes something like this
const addNewItem = (id) => setSelections((prev) => {
const updated = new Set(prev);
updated.add(id);
return updated;
});
That creates a new Set instance of the old one, so all updates trigger down the state tree. You can do the same thing with Map.