2

So I'm trying to enable a button, after two fields have been selected, but for some reason, I have to click on the second field twice to trigger it to enabled. Can anyone see what I'm doing wrong?

So to confirm, when I click on a price and when I click on donateTo field, it is setting state correctly, however it doesn't enable the bottom at the bottom until I have clicked one of the fields a third or fourth time.

const DonateBox = ({ goToPayment }) => {
  const [price, setPrice] = useState('');
  const [donateTo, setDonateTo] = useState('');
  const [isProgressDisabled, setDisabled] = useState(true);

  const handleSetDonateTo = (e) => {
    setDonateTo(e.target.value);
    disabledCheck();
  };

  const handlePriceClick = (e) => {
    setPrice(e.target.value);
    disabledCheck();
  };

  const handleGoToPayment = () => {
    goToPayment('paymentbox');
  };

  const disabledCheck = () => {
    if (price && donateTo) {
      setDisabled(false);
    }
    console.log(isProgressDisabled);
  };

  return (
    <>
      <div className={styles.donateBox}>
        <p className={styles.heading}>
          Give through the most effective platform for good.
        </p>
        <p className={styles.subheading}>
          100% goes to the causes listed in the article here. You are one click
          away!
        </p>
        <div className={styles.itemList}>
          <label className={styles.labelWrapper}>
            <input
              type="radio"
              className={styles.customRadio}
              value="cause"
              onClick={handleSetDonateTo}
              checked={donateTo === 'cause'}
            />
            <span>Donate to Cause</span>
          </label>
          <label className={styles.labelWrapper}>
            <input
              type="radio"
              className={styles.customRadio}
              value="charity"
              onClick={handleSetDonateTo}
              checked={donateTo === 'charity'}
            />
            <span>Donate to Charity</span>
          </label>
          <label className={styles.labelWrapper}>
            <input
              type="radio"
              className={styles.customRadio}
              value="goods"
              onClick={handleSetDonateTo}
              checked={donateTo === 'goods'}
            />
            <span>Donate to Goods</span>
          </label>
        </div>
        <div className={styles.priceList}>
          <label
            className={`${styles.priceLabel} ${
              price === '25' ? `${styles.selected}` : ''
            }`}
          >
            <input
              type="radio"
              name="25"
              className={styles.priceInput}
              onClick={handlePriceClick}
              value="25"
            />
            $25
          </label>
          <label
            className={`${styles.priceLabel} ${
              price === '50' ? `${styles.selected}` : ''
            }`}
          >
            <input
              type="radio"
              name="50"
              className={styles.priceInput}
              onClick={handlePriceClick}
              value="50"
            />
            $50
          </label>
          <label
            className={`${styles.priceLabel} ${
              price === '75' ? `${styles.selected}` : ''
            }`}
          >
            <input
              type="radio"
              name="75"
              className={styles.priceInput}
              onClick={handlePriceClick}
              value="75"
            />
            $75
          </label>
        </div>
      </div>
      <button
        className={styles.largeButton}
        onClick={handleGoToPayment}
        disabled={isProgressDisabled}
      >
        Add Card Details
      </button>
    </>
  );
};

export default DonateBox;

2 Answers 2

1

In addition to @adesurirey's suggestion, this is the perfect scenario for useEffect.

When the variable(s) inside of the dependency array are changed, the code in useEffect is invoked.

Meaning, you can automatically invoke the disabledCheck if price or donateTo are changed. This way you don't have to remember to call disabledCheck within the handlers.

Edit another excellent thing about useEffect in a scenario like this is that you know for sure disabledCheck is invoked only after state is changed. Since mutating state is async, it is possible that disabledCheck was running before state was actually updated.

Think of useEffect like the callback function within setState (on class based components)..

// Class based comparison...
// You know for sure `disabledCheck` is only called *after* 
// state has been updated
setState({ 
  some: 'newState' 
}, () => disabledCheck());

Demo:

const { useState, useEffect, Fragment } = React;
const { render } = ReactDOM;

const DonateBox = ({ goToPayment }) => {
  const [price, setPrice] = useState('');
  const [donateTo, setDonateTo] = useState('');
  const [isProgressDisabled, setDisabled] = useState(true);

  const handleSetDonateTo = (e) => {
    setDonateTo(e.target.value);
    // disabledCheck();
  };

  const handlePriceClick = (e) => {
    setPrice(e.target.value);
    // disabledCheck();
  };

  const handleGoToPayment = () => {
    goToPayment('paymentbox');
  };

  const disabledCheck = () => {
    if (price !== '' && donateTo !== '') {
      setDisabled(false);
    }
    console.log(isProgressDisabled);
  };
  
  useEffect(() => {
    disabledCheck();
  }, [price, donateTo]);

  return (
    <Fragment>
      <div>
        <p>
          Give through the most effective platform for good.
        </p>
        <p>
          100% goes to the causes listed in the article here. You are one click
          away!
        </p>
        <div>
          <label>
            <input
              type="radio"
              value="cause"
              onClick={handleSetDonateTo}
              checked={donateTo === 'cause'}
            />
            <span>Donate to Cause</span>
          </label>
          <label>
            <input
              type="radio"
              value="charity"
              onClick={handleSetDonateTo}
              checked={donateTo === 'charity'}
            />
            <span>Donate to Charity</span>
          </label>
          <label>
            <input
              type="radio"
              value="goods"
              onClick={handleSetDonateTo}
              checked={donateTo === 'goods'}
            />
            <span>Donate to Goods</span>
          </label>
        </div>
        <div>
          <label>
            <input
              type="radio"
              name="25"
              onClick={handlePriceClick}
              value="25"
            />
            $25
          </label>
          <label>
            <input
              type="radio"
              name="50"
              onClick={handlePriceClick}
              value="50"
            />
            $50
          </label>
          <label>
            <input
              type="radio"
              name="75"
              onClick={handlePriceClick}
              value="75"
            />
            $75
          </label>
        </div>
      </div>
      <button
        onClick={handleGoToPayment}
        disabled={isProgressDisabled}
      >
        Add Card Details
      </button>
    </Fragment>
  );
};

const App = () => <DonateBox />

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

Sign up to request clarification or add additional context in comments.

1 Comment

Awesome! Glad I was able to help. One last thing, which I also updated in my answer, but another excellent thing about useEffect in a scenario like this is that you know for sure disabledCheck is invoked only after state is changed. Since mutating state is async, it is possible that disabledCheck was running before state was actually updated.
1

You should use onChange events on your inputs instead of onClick

<input
  type="radio"
  name="75"
  className={styles.priceInput}
  onChange={handlePriceClick}
  value="75"
/>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.