Search code examples
javascriptreactjstypescriptradio-button

React - dynamically update UI based on radio button selection


Question

I'm working on a web application feature that calculates the heat insulation factor for a specific area. You can check out the live Codesandbox example here (it's the third red area with the radio buttons).

The issue I'm facing is that when a user clicks on a radio button, I can see in the console logs that the state updates correctly. However, the "Factor" box on the right side of the page doesn't consistently update when a user clicks on a radio button. I'd like it to update every time a radio button is clicked. How can I achieve this?

Insulation Factor component:

export const InsulFactor = () => {
  // Pull in state from Zustand global store
  const {
    insul,
    wellInsul,
    poorlyInsul,
    notInsul,
    setInsul,
    setWellInsul,
    setPoorlyInsul,
    setNotInsul,
  } = useInsulFactorStore();

  const handleInsulChange = () => {
    setInsul(2);
  };

  const handleWellInsulChange = () => {
    setWellInsul(4);
  };

  const handlePoorlyInsulChange = () => {
    setPoorlyInsul(7);
  };

  const handleNotInsulChange = () => {
    setNotInsul(8.5);
  };

  const getInsulFactor = () => {
    if (insul) {
      return insul;
    }
    if (wellInsul) {
      return wellInsul;
    }
    if (poorlyInsul) {
      return poorlyInsul;
    }
    if (notInsul) {
      return notInsul;
    }
    return insul;
  };

  return (
    <>
     ...
          <Row>
            <Col lg="4">
              <CheckBox
                heading="Insulated"
                desc="Enclosed area with doors and windows in place."
                id="insulated"
                name="insulation"
                checked={insul === 2}
                onChange={handleInsulChange}
              />
            </Col>
            <Col lg="4">
              <CheckBox
                heading="Well Insulated"
                desc="Sealed environment. Walls constructed but lacking insulation. Doors and windows shielded with plastic sheets or tarps."
                id="well-insulated"
                name="insulation"
                checked={wellInsul === 4}
                onChange={handleWellInsulChange}
              />
            </Col>
            <Col lg="4">&nbsp;</Col>
          </Row>
          <Row>
            <Col lg="4">
              <CheckBox
                heading="Poorly Insulated"
                desc="Semi-enclosed area with numerous walls in place."
                id="poorly-insulated"
                name="insulation"
                checked={poorlyInsul === 7.5}
                onChange={handlePoorlyInsulChange}
              />
            </Col>
            <Col lg="4">
              <CheckBox
                heading="Not Insulated"
                desc="Uncovered area with primary walls not yet built."
                id="not-insulated"
                name="insulation"
                checked={notInsul === 8}
                onChange={handleNotInsulChange}
              />
            </Col>
            <Col lg="4">
              <Result heading="Factor" number={getInsulFactor()} />
            </Col>
          </Row>
        ...
    </>
  );
};

Radio component:

export const CheckBox: React.FC<Props> = ({
  heading,
  desc,
  id,
  name,
  onChange,
  checked,
}) => {
  return (
    <>
      <Form.Group>
        <Form.Check
          type="radio"
          label=""
          id={id}
          name={name}
          onChange={onChange}
          defaultChecked={checked}
        />
        <h3>{heading}</h3>
        <p>{desc}</p>
      </Form.Group>
    </>
  );
};

Solution

  • Reviewing the code I think the issue is in the useInsulFactorStore You have multiple levels of insul which then conflict with each other as your getInsulFactor function will return the first truthy value.

    Instead, you should change your context to only have one insul/setInsul state that then updates depending on the selected option. I made some edits to the CodeSandBox here with these changes so you can check it out.