Supposed that I have a complex state with tones of fields that are expensive to copy which I want to update in a certain order. The "supposedly" correctly way is probably
setState({
...myComplexState,
expensiveFieldA: newA,
});
// some logic to compute newB
setState({
...myComplexState,
expensiveFieldB: newB,
});
This not only triggers unnecessary re-renders but also wastes cpu cycles on coping unchanged fields, making the alternative pattern appealing
import { useState } from 'react';
class StateWrapper<S> {
state: S;
constructor(state: S) {
this.state = state;
}
shallowCopy() {
return new StateWrapper(this.state);
}
}
function useObjState<T>(initState: T): [T, () => void] {
const [wrapper, setWrapper] = useState(() => new StateWrapper(initState));
const commit = () => setWrapper(wrapper.shallowCopy());
return [wrapper.state, commit];
}
class ExpensiveState {
private val: string;
constructor() {
this.val = '';
}
preformExpensiveOperations(val: string) {
this.val = val;
}
preformAnotherExpensiveOperation() {}
getVal() {
return this.val;
}
}
function App() {
const [state, commit] = useObjState(new ExpensiveState());
return (
<>
<p>val: {state.getVal()}</p>
<p>
<input
onChange={e => {
state.preformExpensiveOperations(e.target.value);
state.preformAnotherExpensiveOperation();
commit();
}}
/>
</p>
</>
);
}
export default App;
I know it's anti-pattern but why is it discouraged?