You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中用Zustand更新elements列表遇‘state.elements不可迭代’错误

问题:Zustand更新elements列表时触发state.elements is not iterable错误

我在Next.js应用中使用Zustand做状态管理,已经添加了若干状态函数,但每次尝试更新elements列表时,都会触发类似state.elements is not iterable的错误。我已经按照Zustand官方文档中的Next.js配置完成了相关上下文设置。

相关代码

export type ProjectState = {
    projectId: string
    projectName: string
    projectDescription: string
    zoomLevel: number
    scrollLeft: number
    scrollTop: number
    elements: ProjectElementInstance[]
}

export type ProjectActions = {
    fetch: (projectId: string) => void
    updateProjectName: (name: string) => void
    updateZoomLevel: (zoomin: boolean, multiplier: number) => void
    updateScrollLeft: (scrollLeft: number) => void
    updateScrollTop: (scrollTop: number) => void
    addElement: (element: ProjectElementInstance, x?: number, y?: number) => void
    updateElement: (id: string, element: ProjectElementInstance) => void
    selectedElements: () => ProjectElementInstance[]
    updateSelectedElements: (selectedElements: ProjectElementInstance[]) => void
    //     selectedElements: ProjectElementInstance[]
    //     changeSelectedElements: (selectedElements: ProjectElementInstance[]) => void
}

export type ProjectStore = ProjectState & ProjectActions

export const defaultInitState: ProjectState = {
    projectId: "",
    projectName: "",
    projectDescription: "",
    zoomLevel: 1,
    scrollLeft: 0,
    scrollTop: 0,
    elements: [] as ProjectElementInstance[]
}
const updateZoomLevel = (zoomLevel: number, zoomIn: boolean, multiplier: number) => {
    const MIN_ZOOM = 0.05;
    const MAX_ZOOM = 5;
    if ((zoomLevel * multiplier >= MAX_ZOOM && zoomIn) ||
        (zoomLevel / multiplier <= MIN_ZOOM && !zoomIn)
    ) {
        return zoomLevel;
    }
    if (zoomIn) return zoomLevel * multiplier;
    return zoomLevel / multiplier;
}

const updateSelectedElements = (elements: ProjectElementInstance[], selectedElements: ProjectElementInstance[]) => {
    return elements.map((el) => selectedElements.find((sel) => sel.id === el.id) ? { ...el, selected: true } : { ...el, selected: false })
}

export const createProjectStore = (
    initState: ProjectState = defaultInitState,
) => {
    return createStore<ProjectStore>()((set, get) => ({
        ...initState,
        fetch: async (projectId: string) => {
            const project = await getProjectData(projectId)

            set({ projectId, projectName: project?.name, projectDescription: project?.description || "", elements: JSON.parse(project?.content ?? "") || [] })
            console.log(get().elements)
        },
        updateProjectName: (name: string) => set({ projectName: name }),
        updateZoomLevel(zoomin, multiplier) {
            set((state) => ({ zoomLevel: updateZoomLevel(state.zoomLevel, zoomin, multiplier) }))
        },
        updateScrollLeft: (scrollLeft: number) => set({ scrollLeft }),
        updateScrollTop: (scrollTop: number) => set({ scrollTop }),
        addElement: (element: ProjectElementInstance, x?: number, y?: number) => set((state) => ({
            elements: [...state.elements, element]
        })),
        updateElement: (id: string, element: ProjectElementInstance) => set((state) => ({
            elements: state.elements.map((el) => el.id === id ? element : el)
        })),
        selectedElements: () => {
            return []
            // return elements.filter((el) => el.selected)
        },
        updateSelectedElements: (selectedElements: ProjectElementInstance[]) => {
            set((state) => ({
                ...state,
                elements: updateSelectedElements(state.elements, selectedElements)
            }))
        },
    }))
}

问题原因与解决方案

核心原因

错误的根源是state.elements并非预期的数组类型,常见触发场景:

  1. fetch方法中JSON解析异常:JSON.parse(project?.content ?? "")如果遇到无效JSON会抛出错误,或者解析结果不是数组(比如是对象),导致elements被设置为非数组值。
  2. 初始化或外部传入错误:如果调用createProjectStore时传入的initState中elements不是数组,也会导致后续操作失败。

修复方案

  1. 修复fetch方法的JSON解析逻辑:添加错误处理与类型校验,确保elements始终是数组:
fetch: async (projectId: string) => {
    const project = await getProjectData(projectId)
    let elements: ProjectElementInstance[] = []
    try {
        if (project?.content) {
            const parsedContent = JSON.parse(project.content)
            // 验证解析结果是否为数组
            if (Array.isArray(parsedContent)) {
                elements = parsedContent
            }
        }
    } catch (error) {
        console.error("解析项目内容失败:", error)
    }
    set({ 
        projectId, 
        projectName: project?.name, 
        projectDescription: project?.description || "", 
        elements 
    })
},
  1. 添加数组类型守卫:在所有操作elements的方法中,先校验类型避免报错:
    比如修改addElement:
addElement: (element: ProjectElementInstance) => set((state) => ({
    elements: Array.isArray(state.elements) ? [...state.elements, element] : [element]
})),
  1. 校验初始化参数:确保createProjectStore接收的initState中elements是数组:
export const createProjectStore = (
    initState: Partial<ProjectState> = {},
) => {
    const safeInitState = {
        ...defaultInitState,
        ...initState,
        // 强制确保elements是数组
        elements: Array.isArray(initState.elements) ? initState.elements : defaultInitState.elements
    }
    return createStore<ProjectStore>()((set, get) => ({
        ...safeInitState,
        // 后续方法保持不变
    }))
}

内容的提问来源于stack exchange,提问作者Hamilton Hardy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 17:56:03