I want to update the state at the end of a list of conditional updates and accumulates the changes to be committed at once (to avoid async issues with setState).
interface IMyComponentState {
a: string;
b: string;
c: string;
}
(...)
updateState = (condition1: boolean, condition2: boolean) => {
const stateChanges: Partial<IMyComponentState> = {};
if (condition1) {
stateChanges.a = 'someValue 1';
}
if (condition2) {
stateChanges.b = 'someValue 2';
}
this.setState(
{ ...this.state, ...stateChanges },
() => this.doSomethingThatDependsOnState()
);
}
This works fine, but is there a way to do it without using this.state
, like below?
this.setState(
{...stateChanges},
(...)
);
Here tslint
is complaining that setState
expects an object of type Pick<IMyComponentState, "a" | "b" | "c">
but that would require me to specify (and predict) the properties to be changed in advance, which is not possible. I heard that React's diffing algorithm checks the references, but I'm still concerned that putting the whole state
object in setState
would add an extra overhead or is unnecessary.
First of all, you don't need to spread this.state
, React will only apply state changes to the specified keys.
Secondly, the type for setState
intentionally doesn't use Partial<T>
, this is because setting undefined
on a key explicitly will perform a state update, so it uses Pick
(GitHub issue here talking more about it)
To get around this issue, you can cast you state update to Pick<IMyComponentState, keyof IMyComponentState>
;
updateState = (condition1: boolean, condition2: boolean) => {
const stateChanges = {};
if (condition1) {
stateChanges.a = 'someValue 1';
}
if (condition2) {
stateChanges.b = 'someValue 2';
}
this.setState(
{ ...stateChanges } as Pick<IMyComponentState, keyof IMyComponentState>,
() => this.doSomethingThatDependsOnState()
);
}