skip to Main Content

I’m making a fetch request that sends back a JSON with an Array of Documents and inside the array of Documents I get another array of "Keywords". I am able to save the Documents array with React Context but I get an error when trying to save the Keywords array. The error I get is the following: "Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘Keywords’)".

This is an example of the response I get so you can see the structure of the JSON:
WorkView Context - Image from Google Chrome React Dev Tools Extension

Here is my code:

Dashboard.jsx -> Here is where I make the fetch request.

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) {
        localStorage.setItem("workview", JSON.stringify(json.Documents));
        localStorage.setItem(
          "keywords",
          JSON.stringify(json.Documents[0].Keywords)
        );
        dispatch(
          { type: "SET_WORKVIEW", payload: json.Documents },
          {
            type: "SET_KEYWORDS",
            payload: JSON.stringify(json.Documents[0].Keywords),
          }
        );
      }
    };

    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 {
        workview: action.payload,
      };
    case "SET_KEYWORDS":
      return {
        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>
  );
};

Using the save to local storage function I was able to save the Documents[0].Keywords array but I can’t save it into the React Context. In this example I saved Keywords array of the first record of Documents but the end result is to save all the keywords records of all the documents records.

const json = await response.json();

      if (response.ok) {
        localStorage.setItem("workview", JSON.stringify(json.Documents));
        localStorage.setItem(
          "keywords",
          JSON.stringify(json.Documents[0].Keywords)
        );
        dispatch(
          { type: "SET_WORKVIEW", payload: json.Documents },
          {
            type: "SET_KEYWORDS",
            payload: JSON.stringify(json.Documents[0].Keywords),
          }
        );
      }

Image of Local Storage:
Local Storage Image

The purpose of this is to use the Keywords array to create a table were the Keywords Name is the Column Name and the Keywords Value is the table data.

Can someone tell me what I’m doing wrong?

2

Answers


  1. Here’s what you dispatch as keywords:

    JSON.stringify(json.Documents[0].Keywords)
    

    This is a string (JSON.stringify), not an Array.

    You then try to iterate over keywords:

    keywords.map((keyword, index) => (...))
    

    As keywords is a string, there’s nothing to map.

    If I had to bet, your solution would be to dispatch the array itself instead:

      {
                type: "SET_KEYWORDS",
                payload: json.Documents[0].Keywords
      }
    
    Login or Signup to reply.
  2. The issue seems to be with how you are calling the dispatch function when updating the keywords state in your workviewReducer. You are passing two separate objects to the dispatch function, which is not the correct way to update multiple state properties at once using useReducer.

    Here’s how you can fix it:

    1. Change the dispatch call in your Dashboard.jsx to the following:
    dispatch({
      type: "SET_WORKVIEW",
      payload: json.Documents,
      keywords: json.Documents[0].Keywords, // add keywords to the payload
    });
    
    1. Modify your workviewReducer to accept the keywords property in the payload:
    export const workviewReducer = (state, action) => {
      switch (action.type) {
        case "SET_WORKVIEW":
          return {
            ...state,
            workview: action.payload,
            keywords: action.keywords, // add keywords to the state
          };
        default:
          return state;
      }
    };
    

    This way, you can pass both the workview and keywords properties in the same payload object and update them in a single call to dispatch.

    It seems like you’re trying to store the keywords array in local storage, but you are not retrieving it in your code. If you want to use the keywords array from local storage, you can add the following line after retrieving the workview array from local storage:

    const keywordsFromStorage = JSON.parse(localStorage.getItem("keywords"));
    

    Then you can use keywordsFromStorage instead of json.Documents[0].Keywords in your dispatch call.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search