0

I'm trying to fetch data for a React component and set it as a nested object in state:

import React from 'react';
import './App.css';
import XLSX from "xlsx";

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      isLoading:false,
      cards:{}
    }
  }

  componentDidMount(){
    this.setState({isLoading:true});

    /* convert xlsx to JSON */
    fetch("../data/Cards_v0.1.xlsx")
    .then((d)=> d.arrayBuffer()).then((d)=>{

      const data = new Uint8Array(d);
      const workbook = XLSX.read(data, {type:"buffer"});
      const sheet = workbook.Sheets["Deck"];
      const cardsJSON = XLSX.utils.sheet_to_json(sheet,{range:1});

      let cards = {};
      for(let i=0; i<cardsJSON.length; i++){
        for(let j=0; j<cardsJSON.length; j++){
          cards[cardsJSON[i].Name] = cardsJSON[i];
        }
      }
      this.setState({cards:cards, isLoading:false});
  });
  }

  render() {
    if(this.state.isLoading){
      return <div>Loading</div>;
    }
    return (
      <div>
        { this.state.cards.Single.Name }
      </div>
    );
  }
}

export default App;

React devtools shows that the object is in state, with cards>Single>Name being "Single", but {this.state.cards.Single.Name} throws TypeError: Cannot read property 'Name' of undefined.

What's confusing me most is that {this.state.cards.Single} instead throws Objects are not valid as a React child (found: object with keys {Name, Type, Rarity, Text, Money}). If you meant to render a collection of children, use an array instead.

So the key Name is found when it's not being called, but then the object becomes undefined when I call it?

Very confused. Any help is appreciated!

0

1 Answer 1

1

React doesn't know how to display objects, therefore, {this.state.cards.Single} will throw Objects are not valid as a React child.

You also have some odd choice of setting React state. Since the component is always going to fetch data on mount, it makes more sense to make isLoading to be defaulted to true, then set to false on successful fetch response.

I don't know how your cardsJSON is structured, but the example below shows two ways to display nested JSON.

  • Wrapping it in pre, code html elements and using JSON.stringify(obj, replacer, spaces)
  • Destructing the object properties from this.state.cards (if any of these are properties that are also nested objects, then they'll also need to be destructed as well!) and then displaying all destructed data in a table, list, etc.

Working example: https://codesandbox.io/s/km3wwvqqzv

Example.js

import React, { Component, Fragment } from "react";
import DisplayCode from "./displayCode";
import DisplayList from "./displayList";

export default class Example extends Component {
  state = {
    isLoading: true,
    cards: {}
  };

  componentDidMount = () => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(response => response.json())
      .then(json => this.setState({ cards: json, isLoading: false }));
  };

  render = () =>
    this.state.isLoading ? (
      <div>Loading</div>
    ) : (
      <Fragment>
        <DisplayCode cards={this.state.cards} />
        <DisplayList cards={this.state.cards} />
      </Fragment>
    );
}

displayCode.js

import React, { Fragment } from "react";

export default ({ cards }) => (
  <Fragment>
    <h3>Display JSON as code:</h3>
    <pre style={{ height: 300, overflowY: "auto" }}>
      <code>{JSON.stringify(cards, null, 4)}</code>
    </pre>
  </Fragment>
);

displayList.js

import map from "lodash/map";
import React, { Fragment } from "react";

export default ({ cards }) => (
  <Fragment>
    <h3 style={{ marginTop: 30 }}>Display JSON as list:</h3>
    <ul style={{ height: 300, overflowY: "auto" }}>
      {map(
        cards,
        ({
          id,
          name,
          username,
          email,
          address: {
            street,
            suite,
            city,
            zipcode,
            geo: { lat, lng }
          }
        }) => (
          <li key={id}>
            <strong>id:</strong> {id}
            <ul>
              <li>
                <strong>Username:</strong> {username}
              </li>
              <li>
                <strong>Name:</strong> {name}
              </li>
              <li>
                <strong>Email:</strong> {email}
              </li>
              <li>
                <strong>Street: </strong>
                {street}
              </li>
              <li>
                <strong>Suite: </strong>
                {suite}
              </li>
              <li>
                <strong>City: </strong>
                {city}
              </li>
              <li>
                <strong>Zipcode: </strong>
                {zipcode}
              </li>
              <li>
                <strong>Lat: </strong>
                {lat}
              </li>
              <li>
                <strong>Lng: </strong>
                {lng}
              </li>
            </ul>
          </li>
        )
      )}
    </ul>
  </Fragment>
);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.