各位开发者好,
我是初级开发人员,我正在处理一个带有大嵌套对象的项目(示例如下(,理想情况下,我想将其用作可以从多个地方访问的反应状态,但我不知道哪种方法最适合这个解决方案,因为我还没有太多经验。如果你能给我一些建议,代码示例或链接到一些教程或博客,这将是非常有帮助的。
我目前的方法(下面的例子(是在没有useReducer的情况下使用useContext,因为我不想只导出调度,因为我认为这有点难看或对程序员不友好,因为你总是必须定义不同的类型和有效载荷。也许我可以把dispatch封装在函数中,但我只是不确定。我也想过使用Redux,但我还没有使用它,根据阅读的内容,它可能会被高估。
提前谢谢。
{
id: string,
name: string,
sections: [{
id: string,
pos_x: number,
pos_y: number,
rows: number,
columns: number,
curvature: number,
skew: number,
rotation: number,
section_type: string,
name: string,
seats: [{
id: string,
row: number,
column: number,
is_disabled: boolean,
category: string,
name: string,
created_at: string,
updated_at: string,
is_reserved: boolean,
reserved_at: string,
status: string,
ticket: number
}],
total_seats: number,
created_at: string,
updated_at: string,
available_seats: number,
purchased_seats: number,
reserved_seats: number
}],
creator: number,
account: number,
is_editable: boolean,
total_seats: number,
created_at: string,
updated_at: string,
event: number,
available_seats: number,
purchased_seats: number,
reserved_seats: number
}
import React, {createContext, FC, useState} from 'react';
import {Seat, SeatMap, Section} from "../../types/seatmapData";
type UpdateSelectedSectionProps = (section: Section, seatIndex: number) => Section
export interface ISeatMap {
id: string,
name: string,
sections: Section[],
creator: number,
account: number,
isEditable: boolean,
totalSeats: number,
createdAt: string,
updatedAt: string,
event: number,
availableSeats: number,
purchasedSeats: number,
reservedSeats: number
}
export interface ISeatMapContext extends ISeatMap {
setName: (name: string) => void;
setSections: (section: Section[]) => void;
setId: (id: string) => void;
setCreator: (creator: number) => void;
setAccount: (account: number) => void;
setIsEditable: (isEditable: boolean) => void;
setTotalSeats: (totalSeats: number) => void;
setCreatedAt: (createdAt: string) => void;
setUpdatedAt: (updatedAt: string) => void;
setEvent: (event: number) => void;
setAvailableSeats: (availableSeats: number) => void;
setPurchasedSeats: (purchasedSeats: number) => void;
setReservedSeats: (reservedSears: number) => void;
addSectionToSelected: (section: Section) => void;
removeSectionsFromSelected: (section: Section) => void;
clearSelectedSections: () => void;
addSeatToSelected: (section: Section, seat: Seat) => void;
removeSeatFromSelected: (section: Section, seat: Seat) => void;
clearSelectedSeats: () => void;
addSection: (section: Section) => void;
removeSection: (section: Section) => void;
removeSelectedSections: () => void;
updateSection: (section: Section) => void;
updateSelectedSeats: (updateFunction: UpdateSelectedSectionProps) => boolean;
reserveSelectedSeats: (reserved: boolean, reservedAt: Date) => void;
purchaseSelectedSeat: () => void;
disableSelectedSeats: (disabled: boolean) => void;
getSeatMap: () => void;
}
export const SeatMapContext = createContext<ISeatMapContext>(null!);
export interface SeatMapProviderProps extends FC {
children: any;
initialValue: SeatMap;
}
const SeatMapProvider = ({ children, initialValue }: SeatMapProviderProps) => {
const [name, setName] = useState<string>(initialValue.name)
const [sections, setSections] = useState<Section[]>(initialValue.sections)
const [id, setId] = useState<string>(initialValue.id)
const [creator, setCreator] = useState<number>(initialValue.creator)
const [account, setAccount] = useState<number>(initialValue.account)
const [isEditable, setIsEditable] = useState<boolean>(initialValue.is_editable)
const [totalSeats, setTotalSeats] = useState<number>(initialValue.total_seats)
const [createdAt, setCreatedAt] = useState<string>(initialValue.created_at)
const [updatedAt, setUpdatedAt] = useState<string>(initialValue.updated_at)
const [event, setEvent] = useState<number>(initialValue.event)
const [availableSeats, setAvailableSeats] = useState<number>(initialValue.available_seats)
const [purchasedSeats, setPurchasedSeats] = useState<number>(initialValue.purchased_seats)
const [reservedSeats, setReservedSeats] = useState<number>(initialValue.reserved_seats)
const [selectedSections, setSelectedSections] = useState<Section[]>([]);
const [selectedSeats, setSelectedSeats] = useState<{section: Section, seat: Seat}[]>([]);
const addSectionToSelected = (section: Section) => {
setSelectedSections(prev => [...prev, section])
}
const removeSectionsFromSelected = (section: Section) => {
setSelectedSections(prev => prev.filter(s => s.id === section.id))
}
const clearSelectedSections = () => {
setSelectedSections([])
}
const addSeatToSelected = (section: Section, seat: Seat) => {
setSelectedSeats(prev => [...prev, {section: section, seat: seat}])
}
const removeSeatFromSelected = (section: Section, seat: Seat) => {
setSelectedSeats(prev => prev.filter(s => s.section.id === section.id && s.seat.id == seat.id))
}
const clearSelectedSeats = () => {
setSelectedSeats([])
}
const addSection = (section: Section) => {
setSections(prevState => [...prevState, section])
}
const removeSection = (section: Section) => {
setSections(prevState => prevState.filter(s => s.id == section.id))
}
const removeSelectedSections = () => {
if(selectedSections.length == 0) return
setSections(prevState => {
const notSelected: Section[] = []
prevState.forEach(s => {
let isSelected = false
selectedSections.forEach(selected => {
if(selected.id == s.id) isSelected = true
})
if(!isSelected) notSelected.push(s)
})
return notSelected
})
}
const updateSection = (section: Section) => {
setSections((prevState) => {
const index = prevState.findIndex(s => s.id === section.id)
sections[index] = section
return sections
})
}
const updateSelectedSeats = (updateFunction: UpdateSelectedSectionProps): boolean => {
if(selectedSections.length == 0 || selectedSeats.length == 0) return false
const updateTime = new Date().toISOString()
setSections(sections => {
selectedSeats.forEach(({section, seat}) => {
const sectionIndex = sections.findIndex(s => s.id === section.id)
const seatIndex = sections[sectionIndex].seats.findIndex(s => s.id === seat.id)
const newSection = updateFunction(sections[sectionIndex], seatIndex);
sections[sectionIndex] = newSection
sections[sectionIndex].updated_at = updateTime
})
return sections
})
setUpdatedAt(updateTime)
return true
}
const reserveSelectedSeats = (reserved: boolean, reservedAt: Date) => {
const updated = updateSelectedSeats((section, seatIndex) => {
section.seats[seatIndex].is_reserved = reserved
section.seats[seatIndex].reserved_at = reservedAt.toISOString()
return section
})
if(updated) setReservedSeats(prev => reserved ? ++prev : --prev)
}
const purchaseSelectedSeat = () => {
}
const disableSelectedSeats = (disabled: boolean) => {
const updated = updateSelectedSeats((section, seatIndex) => {
section.seats[seatIndex].is_disabled = disabled
if(disabled) section.total_seats--
else section.total_seats++
return section
})
if(updated) setTotalSeats(prev => disabled ? --prev : ++prev)
}
const getSeatMap = (): SeatMap => {
return {
name: name,
sections: sections,
id: id,
creator: creator,
account: account,
is_editable: isEditable,
total_seats: totalSeats,
created_at: createdAt,
updated_at: updatedAt,
event: event,
available_seats: availableSeats,
purchased_seats: purchasedSeats,
reserved_seats: reservedSeats,
}
}
return (
<SeatMapContext.Provider value={{
name, setName,
sections, setSections,
id, setId,
creator, setCreator,
account, setAccount,
isEditable, setIsEditable,
totalSeats, setTotalSeats,
createdAt, setCreatedAt,
updatedAt, setUpdatedAt,
event, setEvent,
availableSeats, setAvailableSeats,
purchasedSeats, setPurchasedSeats,
reservedSeats, setReservedSeats,
addSectionToSelected,
addSeatToSelected,
clearSelectedSeats,
clearSelectedSections,
purchaseSelectedSeat,
removeSeatFromSelected,
removeSectionsFromSelected,
getSeatMap,
addSection,
removeSection,
updateSection,
removeSelectedSections,
updateSelectedSeats,
reserveSelectedSeats,
disableSelectedSeats
}}>
{children}
</SeatMapContext.Provider>
)
};
export default SeatMapProvider;
当我们处理像您这样的大型不可变状态时,状态管理库可以减少痛苦。您应该使用其中一种更适合您的项目的解决方案。是的,上下文很好,但在更新状态时必须小心,因为这会导致渲染整个树(DOM(,并可能导致一些严重的问题。我建议坚持使用redux
并使用reduxtoolkit
库,或者如果你的项目已经用redux编写,你可以使用immer
来更好地使用不可变状态(你可以随时使用immer,即使是上下文(。
如果你想要一些其他的解决方案,我建议你阅读关于可观察状态的文章,例如:recoil
或mobx
。
但如果你仍然想使用上下文,我建议你阅读Kent C.Dods关于上下文的有用文章,以及他创建上下文商店的解决方案。
https://kentcdodds.com/blog/how-to-use-react-context-effectively