React Redux "Cannot read property 'status' of undefined" error when trying to access store variable

Viewed 390

I am working on an app to teach myself React and Redux and I am running into an issue when trying to access the variables that have been added to the store.

I am able to add data to the store object and verify that the data is there by using console.log but when I try to drill down to the actual variable I want to access I am getting an error: "TypeError: Cannot read property 'status' of undefined".

When viewing the state I can see that the status is there: {"customerInfo":[],"loginInfo":[],"storeStatus":[{"status":false}]}

but then trying to access anything within state.storeStatus[0] (example: state.storeStatus[0].status) returns the error of being undefined.

Is there a different way that I should be trying to access that variable if I want to assign it to a new variable to be used in some conditional rendering?

storeStatusReducers.js

import deepFreeze from 'deep-freeze'

const state = [];
const storeStatusReducer = (state = true, action) => {
    switch(action.type) {
      case 'STORE_STATUS_UPDATE':
        state = [];  
        return [...state, action.data]
      default:
      return state
    }
  }

 export const updateStoreStatusAction = (status) => {
    return {
      type: 'STORE_STATUS_UPDATE',
      data: {
        status
      }
    }
  }

  deepFreeze(state)
  export default storeStatusReducer;

home.js

import React,  { useState, useEffect } from "react";
import axios from "axios";
import { updateStoreStatusAction } from '../reducers/storeStatusReducers' 
import { useSelector, useDispatch } from 'react-redux'

const storeInfo = {
  storeId : "1337",
  storeStatus: true
}

const HomePage = (props) => {
  const dispatch = useDispatch()
  const storeStatusData = useSelector(state => state.storeStatus);

  let payload = {
    storeId: storeInfo.storeId,
  };
  useEffect(() => {
  axios({
    url: "/api/get-test-data/" + storeInfo.storeId,
    method: "GET",
    data: payload,
  })
    .then((data) => {
     console.log("data.data.storeStatusPayload", JSON.stringify(data.data.storeStatusPayload))
     updateStoreStatus(data.data.storeStatusPayload);
     
    })
    .catch((error) => {
      console.log("changeStoreStatus: Internal server error");
    });
  }, []);

  const updateStoreStatus = (updatedStoreStatus) => {
    dispatch(updateStoreStatusAction(updatedStoreStatus));
  };

  //This works and produces: {"status":false}
  console.log("storeStatusData", JSON.stringify(storeStatusData[0]))
  // This does not when trying to access status
  console.log("storeStatusData", JSON.stringify(storeStatusData[0].status))


  return (
    <div>
      <main>
      <h1>Test App</h1>

      </main>    
    </div>
  );
};

export default HomePage;

Update: This is working now. Thanks!

Updated home.js

import React,  { useState, useEffect } from "react";
import axios from "axios";
import { updateStoreStatusAction } from '../reducers/storeStatusReducers' 
import { useSelector, useDispatch } from 'react-redux'

const storeInfo = {
  storeId : "1337",
  storeStatus: true
}

const HomePage = (props) => {
  const dispatch = useDispatch()
  const storeStatusData = useSelector(state => state.storeStatus);

  let payload = {
    storeId: storeInfo.storeId,
  };
  useEffect(() => {
  axios({
    url: "/api/get-test-data/" + storeInfo.storeId,
    method: "GET",
    data: payload,
  })
    .then((data) => {
     console.log("data.data.storeStatusPayload", JSON.stringify(data.data.storeStatusPayload))
     updateStoreStatus(data.data.storeStatusPayload);
     
    })
    .catch((error) => {
      console.log("changeStoreStatus: Internal server error");
    });
  }, []);

  const updateStoreStatus = (updatedStoreStatus) => {
    dispatch(updateStoreStatusAction(updatedStoreStatus));
  };

  //This works and produces: {"status":false}
  console.log("storeStatusData", JSON.stringify(storeStatusData[0]))
  // This does not when trying to access status
  console.log("storeStatusData", JSON.stringify(storeStatusData[0].status))

  const renderLoadingBar = () => {
    if (storeStatusData[0] ) {
      return (
        <div>
          loaded
        </div>
      );
    } else {
      return (
        // table container
        <div>
        loading
         </div> 
      );
    }
  };

  return (
    <div>
      <main>
      <h1>Test App</h1>
<div>{renderLoadingBar()}</div>
      </main>    
    </div>
  );
};

export default HomePage;

1 Answers

Since your effect is running asynchronously, your data will not be available on initial render. That means trying to access a prop on the nonexistent array element will cause your app to crash.

The fact that your initial log statement appears to work is mosty likely because it's undefined on the initial render but then the expected value on the second render. On the other hand, your second console.log statement fails immediately and you never make it to a subsequent render.

To prevent this issue from happening, a common pattern is to use a short-circuit evaluation. For example:

const HomePage = (props) => {
  // (initial component code here)

  // Only log if the array element exists
  storeStatusData[0] && console.log("storeStatusData", JSON.stringify(storeStatusData[0].status))


  return (
    <div>
      <main>
      <h1>Test App</h1>

      </main>    
    </div>
  );
};
Related