【问题标题】:Working with big nested object as state in React在 React 中使用大型嵌套对象作为状态
【发布时间】:2022-01-11 19:57:03
【问题描述】:

各位开发者您好,

我是初级开发人员,我正在开发一个包含大型嵌套对象(示例如下)的项目,理想情况下我想将其用作可以从多个地方访问的反应状态,但我不知道哪种方法最好对于这个解决方案,因为我还没有很多经验。如果您能给我一些建议、代码示例或一些教程或博客的链接,那将非常有帮助。

我目前的方法(下面的示例)是使用 useContext without useReducer 因为我只是不想只导出调度,因为我认为有点丑陋或对程序员不友好,因为你总是必须定义不同的类型和有效载荷。也许我可以将 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;

【问题讨论】:

    标签: javascript reactjs typescript react-redux react-hooks


    【解决方案1】:

    当我们处理大型且不可变的状态(例如您正在使用)时,状态管理库可以减轻您的痛苦。您应该使用更适合您的项目的这些解决方案之一。是的,上下文很棒,但是当你更新状态时你必须小心,因为它会导致渲染整个树(DOM)并且可能会给你带来一些严重的问题。我建议坚持使用redux 并使用reduxtoolkit 库,或者如果您的项目已经用redux 编写,您可以使用immer 以获得更好的使用不可变状态的体验(您可以随时使用immer,即使使用上下文)。

    如果您需要其他解决方案,我建议您阅读有关可观察状态的信息,例如:recoilmobx

    但如果您仍想使用上下文,我建议您阅读 Kent C. Dodds 关于上下文的有用文章以及他的使用上下文创建商店的解决方案。

    https://kentcdodds.com/blog/how-to-use-react-context-effectively

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-01
      • 2020-11-13
      • 2021-09-03
      • 2020-01-07
      • 1970-01-01
      • 2021-12-29
      • 2019-03-19
      • 2018-11-08
      相关资源
      最近更新 更多