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

如何用React Context保存嵌套数组?读取Keywords遇TypeError报错

问题

通过fetch请求获取包含Documents数组的JSON数据,每个Document对象嵌套Keywords数组。目前能将Documents数组存入React Context,但保存Keywords数组时触发错误:Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'Keywords')。已成功将第一个Document的Keywords存入localStorage,但无法存入Context,最终需求是保存所有Document的Keywords数组,用于生成以Keywords的Name为列名、Value为表格数据的表格。


错误分析

  1. dispatch调用错误:代码中尝试一次传递两个action对象给dispatch,但React的useReducer dispatch仅支持单个action,第二个action会被忽略,导致SET_KEYWORDS未被正确触发。
  2. 状态覆盖问题:reducer处理SET_WORKVIEW和SET_KEYWORDS时,直接返回新对象而未保留原有状态的其他属性,会导致其中一个状态被覆盖(比如设置workview后,keywords会变为undefined,反之亦然)。
  3. 空值未校验:直接访问json.Documents[0].Keywords未做空值校验,若Documents为空数组,会触发Cannot read properties of undefined错误。
  4. 冗余序列化:存入Context时对Keywords执行JSON.stringify,导致Context中存储的是字符串而非数组,后续map遍历会出错。

修复方案

核心修改点

  • 拆分dispatch调用,分别更新workview和keywords状态
  • reducer中使用扩展运算符...state保留原有状态属性
  • 添加空值校验,避免访问空数组元素触发错误
  • 移除冗余的JSON序列化,直接传递数组到Context
  • 通过flatMap合并所有Document的Keywords数组(满足“保存所有Keywords”的需求)

修改后的代码

Dashboard.jsx

import { useEffect } from "react";
import { useWorkViewContext } from "../hooks/useWorkViewContext";

function Dashboard({ user }) {
  const { workview, keywords, dispatch } = useWorkViewContext();

  useEffect(() => {
    const fetchWorkView = async () => {
      const credentials =
        import.meta.env.VITE_SERVICE_USER +
        ":" +
        import.meta.env.VITE_SERVICE_PASSWORD;
      const response = await fetch(
        "https://urlforfetch.com",
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Basic ${credentials}`,
          },
          body: JSON.stringify({
            WorkViewApplication: "VAPR",
            WorkViewClassName: "Policy",
            WorkViewFilter: "All Policy Record",
            SearchAttributes: [
              {
                Name: "InsuranceCarrier",
                Value: "MAPFRE",
              },
            ],
          }),
        }
      );
      const json = await response.json();

      if (response.ok && json.Documents?.length > 0) {
        // 存入localStorage
        localStorage.setItem("workview", JSON.stringify(json.Documents));
        // 收集所有Document的Keywords数组
        const allKeywords = json.Documents.flatMap(doc => doc.Keywords || []);
        localStorage.setItem("keywords", JSON.stringify(allKeywords));
        
        // 分别调用dispatch更新状态
        dispatch({ type: "SET_WORKVIEW", payload: json.Documents });
        dispatch({ type: "SET_KEYWORDS", payload: allKeywords });
      }
    };

    if (user) {
      fetchWorkView();
    }
  }, [dispatch, user]);

  return (
    <main>
      <div className="dashboard">
        <h1>
          Welcome back, <span className="accent">{user.username}</span>
        </h1>
        <div className="grid-sm">
          <div className="grid-sm">
            <p>In this section you will find you cases information.</p>
          </div>
          <div className="grid-lg">
            <h2>My Cases</h2>
            {workview && workview.length > 0 && (
              <div className="grid-md">
                {workview.map((document, index) => (
                  <a key={index} href={document.DocumentURL}>
                    {document.DocumentName}
                  </a>
                ))}
              </div>
            )}
            {keywords && keywords.length > 0 && (
              <div className="grid-md">
                {keywords.map((keyword, index) => (
                  <a key={index}>{keyword.Name}</a>
                ))}
              </div>
            )}
          </div>
        </div>
      </div>
    </main>
  );
}

export default Dashboard;

WorkViewContext.jsx

import { createContext, useReducer } from "react";

export const WorkViewContext = createContext();

export const workviewReducer = (state, action) => {
  switch (action.type) {
    case "SET_WORKVIEW":
      return {
        ...state, // 保留原有状态
        workview: action.payload,
      };
    case "SET_KEYWORDS":
      return {
        ...state, // 保留原有状态
        keywords: action.payload,
      };
    default:
      return state;
  }
};

export const WorkViewContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(workviewReducer, {
    workview: null,
    keywords: null,
  });

  return (
    <WorkViewContext.Provider value={{ ...state, dispatch }}>
      {children}
    </WorkViewContext.Provider>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 10:50:03