I am trying to decide if it is a good idea to design a React state that contains the logic to update itself (primarily by use of JS class syntax).
Below are two versions of a same Clock component:
import React from "react";
type TickTock = "tick" | "tock";
function Clock() {
const [tick, setTick] = React.useState<TickTock>("tick");
const handleTick = () => {
setTick(state => {
switch (state) {
case "tick":
return "tock";
case "tock":
return "tick";
}
});
};
React.useEffect(() => {
const id = setInterval(handleTick, 1000);
return () => {
clearInterval(id);
};
}, []);
return <div>{tick}</div>;
}
export default Clock;
In this one, the handleTick function looks at the state and decides how to update it.
import React from "react";
interface TickTock {
title: string;
next: TickTock;
}
class Tick implements TickTock {
get title() {
return "tick";
}
get next() {
return new Tock();
}
}
class Tock implements TickTock {
get title() {
return "tock";
}
get next() {
return new Tick();
}
}
function Clock() {
const [tick, setTick] = React.useState<TickTock>(new Tick());
const handleTick = () => {
setTick(state => state.next);
};
React.useEffect(() => {
const id = setInterval(handleTick, 1000);
return () => {
clearInterval(id);
};
}, []);
return <div>{tick.title}</div>;
}
export default Clock;
In that one, the handleTick function merely calls the next getter to make the state update itself. The point is that the handleTick function has no knowledge of the state logic.
The first version is what I have seen done most often and seems the most straightforward. However, there have been cases where I have found the second version appealing, for example when a state gets complex and has only few definite ways of getting updated.
I was wondering therefore if there were drawbacks to the second version.