Home > Back-end >  Update react button disabled state on redux state change
Update react button disabled state on redux state change

Time:08-29

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;

  • Related