I've set up a new project with React, Redux (using toolkit). I've got a button that needs to be disabled if the user does not have enough of a particular resource. I've confirmed that the state is being updated properly and the reducers are applying to state properly, but I am unable to get the button to disable when that resource falls below the supplied price.
I've tried duplicating state from redux using a useState hook, but setting the state within canAfford() still doesn't disable the button. I'm at a bit of a loss, and feel like I'm just missing something about redux state and rendering.
Here's the button component I'm working with:
function BuyBtn({ technology, label, resourceType, price, requirements = []}: IBuyBtn) {
const units = useSelector((state: any) => state.units);
const tech = useSelector((state: any) => state.tech);
const resources = useSelector((state: any) => state.resources);
const dispatch = useDispatch();
let disabled = false;
let unlocked = true;
useEffect(() => {
disabled = !canAfford()
}, [resources])
const canAfford = (): boolean => {
console.log('Units:', units);
console.log("Checking affordability");
if (resourceType.length != price.length) {
throw `BuyBtn Error: price length is ${price.length} but resource length is ${resourceType.length}.`;
}
resourceType.forEach((res, i) => {
const resPrice = price[i];
if (resources[res] < resPrice) {
return false;
}
});
return true;
};
const meetsRequirements = (): boolean => {
if (requirements.length === 0) {
return true;
}
requirements.forEach((req) => {
if (!tech[req]) {
return false;
}
});
return true;
};
const buyThing = () => {
if (canAfford() && meetsRequirements()) {
resourceType.forEach((res, i) => {
const resPrice = price[i];
dispatch(SubtractResource(res, resPrice));
});
dispatch(UnlockTech(technology, true))
}
};
if (meetsRequirements() && canAfford()) {
return (
<button onClick={buyThing} disabled={disabled}>{label}</button>
);
} else {
return null;
}
}
export default BuyBtn;
CodePudding user response:
Instead of using disabled
as variable make it State which will trigger re-render:
function BuyBtn({ technology, label, resourceType, price, requirements = []}: IBuyBtn) {
const units = useSelector((state: any) => state.units);
const tech = useSelector((state: any) => state.tech);
const resources = useSelector((state: any) => state.resources);
const dispatch = useDispatch();
const [disabled, setDisabled] = React.useState(false);
let unlocked = true;
const canAfford = (): boolean => {
console.log('Units:', units);
console.log("Checking affordability");
if (resourceType.length != price.length) {
throw `BuyBtn Error: price length is ${price.length} but resource length is ${resourceType.length}.`;
}
let isAffordable = true
resourceType.forEach((res, i) => {
const resPrice = price[i];
if (resources[res] < resPrice) {
isAffordable = false;
}
});
return isAffordable;
};
useEffect(async() => {
const value = await canAfford();
setDisabled(!value);
}, [resources])
const meetsRequirements = (): boolean => {
if (requirements.length === 0) {
return true;
}
let isMeetingRequirements = true;
requirements.forEach((req) => {
if (!tech[req]) {
isMeetingRequirements = false;
}
});
return isMeetingRequirements;
};
const buyThing = () => {
if (canAfford() && meetsRequirements()) {
resourceType.forEach((res, i) => {
const resPrice = price[i];
dispatch(SubtractResource(res, resPrice));
});
dispatch(UnlockTech(technology, true))
}
};
if (meetsRequirements() && canAfford()) {
return (
<button onClick={buyThing} disabled={disabled}>{label}</button>
);
} else {
return null;
}
}
export default BuyBtn;