how to shrink multiple state function into one in react.js

Viewed 75
import React from "react";
import { AiOutlineArrowDown, AiOutlineArrowUp } from "react-icons/ai";
import { useState } from "react";

const Vote = () => {
  const [num, setnum] = useState(0);

  const numdown = () => {
    num != 0 ? setnum(num - 1) : setnum(num);
  };
  const numup = () => {
    num < 9 ? setnum(num + 1) : setnum(num);
  };
  const [num2, setnum2] = useState(0);

  const numdown2 = () => {
    num2 != 0 ? setnum2(num2 - 1) : setnum(num2);
  };
  const numup2 = () => {
    num2 < 9 ? setnum2(num2 + 1) : setnum2(num);
  };
  var value1 = (
    <>
      <AiOutlineArrowDown onClick={numdown} />
      {num}
      <AiOutlineArrowUp onClick={numup} />
    </>
  );
  var value2 = (
    <>
      <AiOutlineArrowDown onClick={numdown2} />
      {num2}
      <AiOutlineArrowUp onClick={numup2} />
    </>
  );

  return (
    <>
      {value1}
      {value2}
    </>
  );
};

export default Vote;

(in the upper code i have written a code template to form a poll area with two voted values having increament and decreament function.The vote values lies between 0 to 9 as explained in two ternary operator functions) !!!how to shrink this code into one state function and inreament and decrement function with same working??????

3 Answers

It's a good question. Basically, you can handle all duplicated logic using custom hooks.

You can see a live example here: https://codesandbox.io/s/affectionate-sunset-7c9lw6?file=/src/App.js

In this case, it'd use a hook that handles the number, numUp & numDown.

const useNumber = (baseNunber) => {
  const [state, setState] = React.useState(baseNunber || 0);

  const numUp = (sum = 1) => {
    setState((prev) => prev + sum);
  };

  const numDown = (dec = 1) => {
    setState((prev) => prev - dec);
  };

  return [state, numUp, numDown];
};

You can even go further a define a component that handle even the UI, something like:

const NumberRow = ({ baseNumber }) => {
  const [num, numUp, numDown] = useNumber(baseNumber);

  return (
    <div>
      <button onClick={() => numDown()}>-</button>
      {num}
      <button onClick={() => numUp()}>+</button>
    </div>
  );
};

Finally, use the hook and/or component in your code:

export default function App() {
  const [num1, num1Up, num1Down] = useNumber(0);
  const [num2, num2Up, num2Down] = useNumber(0);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={() => num1Down()}>-</button>
      {num1}
      <button onClick={() => num1Up()}>+</button>
      <br />
      <button onClick={() => num2Down()}>-</button>
      {num2}
      <button onClick={() => num2Up()}>+</button>
      <NumberRow baseNumber={0} />
    </div>
  );
}

Important for usability Take into consideration you can even use extra parameters like the amount to numUp or numDown

You should create a single component with counter state and arrow elements and use it twice. Try like this:

import React from "react";
import { AiOutlineArrowDown, AiOutlineArrowUp } from "react-icons/ai";
import { useState } from "react";

const ValueComp = () => {
  const [num, setnum] = useState(0);

  const numdown = () => {
    num != 0 ? setnum(num - 1) : setnum(num);
  };
  const numup = () => {
    num < 9 ? setnum(num + 1) : setnum(num);
  };

  return (
    <>
      <AiOutlineArrowDown onClick={numdown} />
      {num}
      <AiOutlineArrowUp onClick={numup} />
    </>
  );
};

const Vote = () => {
  return (
    <>
      <ValueComp />
      <ValueComp />
    </>
  );
};

export default Vote;

Edit brave-forest-6l4f53

I think you are looking something like this:

App.js

import { useState } from "react";

export default function App() {
  let [data, setData] = useState(0);
  const handleData = (type) => () => {
    setData((prvState) => {
      return type === "up" ? ++prvState : --prvState;
    });
  };

  return (
    <div>
      <button onClick={handleData("up")} style={{ marginRight: "5px" }}>
        Up
      </button>
      {data}
      <button onClick={handleData("down")} style={{ marginLeft: "5px" }}>
        Down
      </button>
    </div>
  );
}

If you want the same thing multiple times then you can create a component and modify this code accordingly.

Run the code in the sandbox I am providing for playing around.

Related