I am converting a class component to a functional component but I am having an issue where state values from useState
are not the most recent values inside a another function. Because the functions need to be called dynamically, they are first defined in a mapping to an ID and invoked using the current ID. Before, I solved this by binding the validator functions to the component itself. However as a functional component, this is not possible.
Here is a simplified version of the issue:
export default function App(props) {
const [currentStage, setStage] = useState(0);
const [stageOrder, setStageOrder] = useState([]);
const [inputName, setInput] = useState(null);
const [invalid, setInvalid] = useState(false);
const stageDefinitions = {
[StageType.ONE]: { id: StageType.ONE, validator: validateOne },
[StageType.TWO]: { id: StageType.TWO, validator: validateTwo },
};
useEffect(() => {
// written like this for readability and make it easier to change the order in the future
let order = props.flag ? [StageType.ONE, StageType.TWO] : [StageType.ONE];
order = order.map((s) => stageDefinitions[s]);
setStageOrder(order);
}, []);
function nextStage() {
if (currentStage === stageOrder.length - 1) return;
const stage = stageOrder[currentStage];
const isValid = stage.validator();
if (isValid) setStage(currentStage + 1);
setInvalid(!isValid);
}
function validateOne() {
// problem is here; inputName remains null when called from nextStage
console.log("inputName", inputName);
return inputName && inputName.length > 1;
}
function validateTwo() {
return props.two > 1;
}
function handleClick() {
setInput(`${Math.random()}`);
}
return (
<div>
<div>Value: {inputName}</div>
{invalid && <div style={{ color: "red" }}>INVALID</div>}
<div>Stage: {currentStage}</div>
<button onClick={handleClick}>Set Value</button>
<button onClick={nextStage}>Next Stage</button>
</div>
);
}
This can also be found on codesandbox.
I’ve tried using useCallback
and defining stageDefinitions
in the state. The ultimate goal is that when a validator is called from nextStage
, it uses the current value of inputName
.