...

/

Using Props With Context

Using Props With Context

Continue refactoring React code with contexts in this lesson.

Row component

The Row component is no longer tracking the state of its seats so it can be much simpler:

Press + to interact
import * as React from "react"
import Seat from "../components/seat"
import { IsVenueContext, VenueContext } from "./app"
interface RowProps {
rowNumber: number
}
const Row = ({ rowNumber }: RowProps) => {
const context = React.useContext<IsVenueContext>(VenueContext)
const seatItems = Array.from(Array(context.state.seatsInRow).keys()).map(
seatNumber => {
return (
<Seat
key={seatNumber + 1}
seatNumber={seatNumber + 1}
rowNumber={rowNumber + 1}
/>
)
}
)
return <tr>{seatItems}</tr>
}
export default Row

Note that we haven’t totally abandoned the use of props—the row number of each individual row isn’t a part of the global state, so we pass it to that row as a prop. Similarly, the Seat component will get its row and seat number as props.

Seat component

The Seat component takes on all the application logic for seat status, and so it picks up some of the complexity that the other components have lost:

Press + to interact
import * as React from "react"
import styled from "styled-components"
import { TicketData } from "./venue_reducer"
import { IsVenueContext, VenueContext } from "./app"
const stateColor = (status: string): string => {
if (status === "unsold") {
return "white"
} else if (status === "held") {
return "green"
} else if (status === "purchased") {
return "red"
} else {
return "yellow"
}
}
interface SquareProps {
status: string
className?: string
}
const ButtonSquare = styled.span.attrs({ className: "button" })<SquareProps>`
background-color: ${props => stateColor(props.status)};
transition: all 1s ease-in-out;
border-width: 3px;
&:hover {
background-color: ${props =>
props.status === "unsold" ? "lightblue" : stateColor(props.status)};
}
`
interface SeatProps {
seatNumber: number
rowNumber: number
}
export const Seat = ({ seatNumber, rowNumber }: SeatProps) => {
const context = React.useContext<IsVenueContext>(VenueContext)
const seatMatch = (ticketList: TicketData[], exact = false): boolean => {
for (const heldTicket of ticketList) {
const rowMatch = heldTicket.row == rowNumber
const seatDiff = heldTicket.number - seatNumber
const diff = exact ? 1 : context.state.ticketsToBuy
const seatMatch = seatDiff >= 0 && seatDiff < diff
if (rowMatch && seatMatch) {
return true
}
}
return false
}
const currentStatus = (): string => {
if (seatMatch(context.state.otherHeldTickets, true)) {
return "purchased"
}
if (seatMatch(context.state.myHeldTickets, true)) {
return "held"
}
if (
seatMatch(context.state.otherHeldTickets) ||
seatMatch(context.state.myHeldTickets) ||
seatNumber + context.state.ticketsToBuy - 1 > context.state.seatsInRow
) {
return "invalid"
}
return "unsold"
}
const onSeatChange = (): void => {
const status = currentStatus()
if (status === "invalid" || status === "purchased") {
return
}
const actionType = status === "unsold" ? "holdTicket" : "unholdTicket"
context.dispatch({ type: actionType, seatNumber, rowNumber })
}
return (
<td>
<ButtonSquare status={currentStatus()} onClick={onSeatChange}>
{seatNumber}
</ButtonSquare>
</td>
)
}
export default Seat

A lot of this is similar to the previous version. The new bits are the currentStatus function and its ...