skip to Main Content

Building a ticket system for a movie app to learn react.
I’ve been trying to wrap my head around the useState and react hooks to make my seating system return an occupied seat after getting clicked on if it is vacant. What am I missing to make this work?

const PurchaseTicket = () => {
  const seat = () => {
    return (
      <div className=' cursor-pointer bg-gray-500 h-[24px] w-[30px] m-1 rounded-t-[10px]'></div>
    )
  }

  const seatOccupied = () => {
    return (
      <div className=' bg-cyan-500 h-[24px] w-[30px] m-1 rounded-t-[10px]'></div>
    )
  }

  const [seating, setSeating] = useState([seat()])

  const vacancy = () => {
    setSeating((seatOccupied()))
  }

  const sideRow = () => {
    return (
      <>
        <div className="m-auto pt-10 flex flex-row">
        <div onClick={vacancy}>{seating}</div>
      </>
    )
  }

  return (
    <>
      <div className='m-15 flex flex-row justify-evenly'>
        <div>{sideRow()}</div>
        <div>{middleRow()}</div>
        <div>{sideRow()}</div>
      </div>
    </>
  )
}

2

Answers


  1. seat and seatOccupied are not hooks. They’re components.

    tl;dr:

    • a component returns JSX, used by ReactDOM to render and update DOM, based on changes to the component’s props and state
    • you’re not supposed to place JSX in state. Instead, place data there and use that data to conditionally render defined components.

    For what you’re trying to achieve, probably the simplest solution would be to have a seat component which has a sold property (among others you might need), along these lines:

    const Seat = (sold) => (
      <div
        className={
          sold
            ? 'cursor-pointer bg-gray-500 h-[24px] w-[30px] m-1 rounded-t-[10px]'
            : 'bg-cyan-500 h-[24px] w-[30px] m-1 rounded-t-[10px]'
        }
      />
    )
    

    And the parent function would map through all seats and pass the sold prop to each, based on rowKey and seatKey, along these lines:

    <>
      {rows.map((row, rowKey) => (
        <div key={rowKey}>
          {row.map((seat, seatKey) => (
            <Seat
              key={seatKey}
              sold={isSeatSold(rowKey, seatKey)}
              onClick={() => toggleSeat(rowKey, seatKey)}
            />
          ))}
        </div>
      ))}
    </>
    
    Login or Signup to reply.
  2. React hook (useState in this context), can store value of any type with special behaviour for functions as said in the docs.

    To make it works, we add additional vacant state (you can use whatever name you like).

    const PurchaseTicket = () => {
      const seat = () => {
        return (
          <div className="cursor-pointer bg-gray-500 h-[24px] w-[30px] m-1 rounded-t-[10px]" />
        )
      }
    
      const seatOccupied = () => {
        return (
          <div className=" bg-cyan-500 h-[24px] w-[30px] m-1 rounded-t-[10px]" />
        )
      }
    
      const [seating, setSeating] = useState(seat)
      const [vacant, setVacant] = useState(true)
    
      const vacancy = () => {
        setVacant((prev) => !prev)
      }
    
      useEffect(() => {
        setSeating(vacant ? seat : seatOccupied);
      }, [vacant])
    
      return (
        <>
          ...
          <div onClick={vacancy}>{seating}</div>
          ...
        </>
      )
    }
    

    but keep in mind that both seat and seatOccupied should be pure, should take no arguments, and should return a value of any type (JSX in this context).

    But I myself, prefers to extract the seat and seatOccupied as separate components

    function Seat() {
      return (
        <div className="cursor-pointer bg-gray-500 h-[24px] w-[30px] m-1 rounded-t-[10px]" />
      )
    }
    
    function SeatOccupied() {
      return <div className="bg-cyan-500 h-[24px] w-[30px] m-1 rounded-t-[10px]" />
    }
    
    

    and render it with conditional rendering:

      return (
        <>
          ...
          <div onClick={vacancy}>{!vacant ? <SeatOccupied /> : <Seat />}</div>
          ...
        </>
      )
    

    or:

    function Seat({ vacant }) {
      return (
        <div
          className={`${!vacant ?"bg-cyan-500" : "cursor-pointer bg-gray-500"} h-[24px] w-[30px] m-1 rounded-t-[10px]`}
        />
      )
    }
    
    

    and render it like this:

    return (
        <>
          ...
          <div onClick={vacancy}>
            <Seat vacant={vacant} />
          </div>
          ...
        </>
      )
    

    So, you can choose whatever you like.

    You can see my example here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search