Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way of updating several states using react hook inside if statement?

I am making a simple app that user can increment or decrement number of adult, child and baby.

I want to manage these state inside one state as a form of object like this below.

  const [numberState, setNumberState] = useState({
    adultCount: 0,
    childCount: 0,
    babyCount: 0
  });

And below code is the part where user can increase or decrease number of adult, child and baby.

            <div className="tit">Number</div>
                <dl>
                    <dt><span>Adult</span></dt>
                        <dd>
                            <div className="number_count">
                                <button type="button" className="btn_minus" onClick={() => handleNumberState("adult", "minus")}>minus</button>
                                <span className="input-num"><input type="number" value={adultCount} /></span>
                                <button type="button" className="btn_plus" onClick={() => handleNumberState("adult", "plus")}>plus</button>
                            </div>
                        </dd>
                    <dt><span>Child</span></dt>
                        <dd>
                            <div className="number_count">
                                <button type="button" className="btn_minus" onClick={() => handleNumberState("child", "minus")}>minus</button>
                                <span className="input-num"><input type="number" value={childCount} /></span>
                                <button type="button" className="btn_plus" onClick={() => handleNumberState("child", "plus")}>plus</button>
                            </div>
                        </dd>
                    <dt><span>Baby</span></dt>
                        <dd>
                            <div className="number_count">
                                <button type="button" className="btn_minus" onClick={() => handleNumberState("baby", "minus")}>minus</button>
                                <span className="input-num"><input type="number" value={babyCount} /></span>
                                <button type="button" className="btn_plus" onClick={() => handleNumberState("baby", "plus")}>plus</button>
                            </div>
                        </dd>
                    </dl>
                </div>

As you can see when user clicks the button, it passes two arguments to handleNumberState callback function.

Then function looks like this below.

const handleNumberState = useCallback((gen, state) => {
  if (gen === "adult" && state === "minus" && numberState.adultCount > 0) {
    setNumberState(prevState => {
      return {
        adultCount: prevState.adultCount - 1,
        childCount: prevState.childCount,
        babyCount: prevState.babyCount
      }
    }) 
  }
  if (gen === "adult" && state === "plus") {
    setNumberState(prevState => {
      return {
        adultCount: prevState.adultCount + 1,
        childCount: prevState.childCount,
        babyCount: prevState.babyCount
      }
    }) 
  }
  if (gen === "child" && state === "minus" && numberState.childCount > 0) {
    setNumberState(prevState => {
      return {
        adultCount: prevState.adultCount,
        childCount: prevState.childCount - 1,
        babyCount: prevState.babyCount
      }
    }) 
  }
  if (gen === "child" && state === "plus") {
    setNumberState(prevState => {
      return {
        adultCount: prevState.adultCount,
        childCount: prevState.childCount + 1,
        babyCount: prevState.babyCount
      }
    }) 
  }
  if (gen === "baby" && state === "minus" && numberState.babyCount > 0) {
    setNumberState(prevState => {
      return {
        adultCount: prevState.adultCount,
        childCount: prevState.childCount,
        babyCount: prevState.babyCount - 1
      }
    }) 
  }
  if (gen === "baby" && state === "plus") {
    setNumberState(prevState => {
      return {
        adultCount: prevState.adultCount,
        childCount: prevState.childCount,
        babyCount: prevState.babyCount + 1
      }
    }) 
  }
})

Depends on what arguments get passed to the function, state is changed inside if statement.

But the code seems messy.

Is there a better way I can make this work using less if statement?

like image 845
GoonGamja Avatar asked Aug 20 '20 06:08

GoonGamja


1 Answers

Update handler to take a field and value instead.

const handleNumberState = (field, value) => {
  setNumberState(prevState => ({
    ...prevState,
    [field]: Math.max(0, prevState[field] + value),
  }))
}

Pass the field name and value to add to the handler.

<div className="tit">Number</div>
<dl>
  <dt>
    <span>Adult</span>
  </dt>
  <dd>
    <div className="number_count">
      <button
        type="button"
        className="btn_minus"
        onClick={() => handleNumberState("adultCount", -1)}
      >
        minus
      </button>
      <span className="input-num">
        <input type="number" value={adultCount} />
      </span>
      <button
        type="button"
        className="btn_plus"
        onClick={() => handleNumberState("adultCount", 1)}
      >
        plus
      </button>
    </div>
  </dd>
  <dt>
    <span>Child</span>
  </dt>
  <dd>
    <div className="number_count">
      <button
        type="button"
        className="btn_minus"
        onClick={() => handleNumberState("childCount", -1)}
      >
        minus
      </button>
      <span className="input-num">
        <input type="number" value={childCount} />
      </span>
      <button
        type="button"
        className="btn_plus"
        onClick={() => handleNumberState("childCount", 1)}
      >
        plus
      </button>
    </div>
  </dd>
  <dt>
    <span>Baby</span>
  </dt>
  <dd>
    <div className="number_count">
      <button
        type="button"
        className="btn_minus"
        onClick={() => handleNumberState("babyCount", -1)}
      >
        minus
      </button>
      <span className="input-num">
        <input type="number" value={babyCount} />
      </span>
      <button
        type="button"
        className="btn_plus"
        onClick={() => handleNumberState("babyCount", 1)}
      >
        plus
      </button>
    </div>
  </dd>
</dl>

Edit better-way-of-updating-several-states-using-react-hook-inside-if-statement

A minor optimization would be to create a curried handler that consumes a value and returns a callback function consuming the click event

const handleNumberState = value => e => {
  e.preventDefault();
  const { name } = e.target;
  setNumberState((prevState) => ({
    ...prevState,
    [name]: Math.max(0, prevState[name] + value)
  }));
};

Give each button a name attribute which is passed with the click event, ex

<button
  name="adultCount"
  type="button"
  className="btn_minus"
  onClick={handleNumberState(-1)}
>
  minus
</button>
<span className="input-num">
  <input type="number" value={adultCount} />
</span>
<button
name="adultCount"
  type="button"
  className="btn_plus"
  onClick={handleNumberState(1)}
>
  plus
</button>
like image 143
Drew Reese Avatar answered Sep 29 '22 01:09

Drew Reese