简体   繁体   中英

JSON Object Property not returnable - “Cannot Read Property of Undefined”

Ok, so I'm building out a custom API in React. When I make the calls, I'm getting JSON data back and store that into local storage with JSON.Stringify:

localStorage.setItem('user', JSON.stringify(response.data))

Later, I call this item onto the Homepage to return some of that data once the user is logged in using:

var user = JSON.parse([localStorage.getItem('user')])

This returns the object:

{
"OrderId":0,
"IsLoggedIn":true,
"ModeOfSaleId":64,
"OriginalModeOfSaleId":64,
"SourceId":8580,
"LoginInfo":{"ConstituentId":190554,"OriginalConstituentId":190554,"UserId":"test@email.org","Status":"P","FailedAttempts":0,"LockedDate":null,"ElectronicAddress":"test@email.org"},
"CartInfo":{"PerformanceCount":0,"PackageCount":0,"ContributionCount":0,"MembershipCount":0,"UserDefinedFeeCount":0,"GiftCertificateCount":0,"PaymentCount":0,"FirstSeatAddedDateTime":null},
"BusinessFacing":false,
"IsGuest":false,
"CheckoutStatus":{"Status":"No Checkout","Date":null},
"HasLockedSeats":false,
"SeatsExpired":false
}

The Issue:

Un-nested properties return normally {user.OrderId} or {user.ModeOfSaleId} However, trying to return the nested values like {user.LoginInfo.ConstituentID} result in the error:

Uncaught TypeError: Cannot read property 'ConstituentId' of undefined

Returning {user.LoginInfo} actually returns an object, but obviously, can't print that to a string. Returning {user.LoginInfo["ConstituentId"]} results in the error:

Uncaught TypeError: Cannot read property 'ConstituentId' of undefined

So yeah, I'm stumped, I don't know how I'm returning this incorrectly. Any help is appreciated.

What about using spread operator to get what you want?

const user = JSON.parse(localStorage.getItem('user'))
const { ConstituentId, UserId } = user.LoginInfo

console.log(ConstituentId) // 190554

This code works for me:

localStorage.setItem("user", JSON.stringify({
   "OrderId":0,
   "IsLoggedIn":true,
   "ModeOfSaleId":64,
   "OriginalModeOfSaleId":64,
   "SourceId":8580,
   "LoginInfo":{"ConstituentId":190554,"OriginalConstituentId":190554,"UserId":"test@email.org","Status":"P","FailedAttempts":0,"LockedDate":null,"ElectronicAddress":"test@email.org"},
   "CartInfo":{"PerformanceCount":0,"PackageCount":0,"ContributionCount":0,"MembershipCount":0,"UserDefinedFeeCount":0,"GiftCertificateCount":0,"PaymentCount":0,"FirstSeatAddedDateTime":null},
   "BusinessFacing":false,
   "IsGuest":false,
   "CheckoutStatus":{"Status":"No Checkout","Date":null},
   "HasLockedSeats":false,
   "SeatsExpired":false
}));

const user = JSON.parse(localStorage.getItem("user"));

console.log(user.LoginInfo.OriginalConstituentId);

Ok, so the way I'm returning these values seems to be an "issue" because of the way React handles it's Render event. When I'm pulling in the data on the componentDidMount() event, a Render event still fires just before this.

componentDidMount() {
  this.setState({ 
    user: JSON.parse([localStorage.getItem('user')]),
    users: { loading: true }
  });
}

So in the Render event:

render() {
    const { user, users, loading } = this.state;
    var { ConstituentId, UserId } = user.LoginInfo


    return (
        <div className="col-md-6 col-md-offset-3">
            <h1>Hi!</h1>
            <p>{UserId}</p>
            <p>You're logged in with React & Basic HTTP Authentication!!</p>
            <h3>Users from secure api end point:</h3>
            <p>
                <Link to="/login">Logout</Link>
            </p>
        </div>
    );
}

It fires TWICE, once BEFORE the state.user is set by componentDidMount() and once again after. So, my code was erroring out because of the first firing of Render, when nothing was set, hence the undefined message. I figured out how to bypass this with checking the login info object is returning as typeof object . This is in my render event:

var result = (typeof user.loginInfo === 'object');

if (result && loading) {
    console.log(result)
    console.log(user.LoginInfo.ConstituentId)
    var { ConstituentId, UserId } = user.LoginInfo
}

But that's not very elegant. So, ultimately I re-wrote how I was handling unloaded information in componentDidMount() by creating a state prop called 'loading':

this.state = {
  loading: true,
  user: {}
};

In componentDidMount() I'm doing this:

this.setState({ 
  user: JSON.parse(localStorage.getItem('user')),
  loading: false
});

And in render() :

const { loading, user } = this.state;
if (!loading) {
  var { ConstituentId, UserId } = user.LoginInfo
}
console.log(ConstituentId)

It works great!

Basically, I'm just waiting for componentDidMount() to fire using the loading state by setting it to false in the function. Then we know it's loaded and can successfully render the data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM