0

Here is the component I am building. I have a dummy backend API that contains a list of fruits, and the response data looks as such:

{
  "data": {
    "1": {
      "apple": "Delicious"
    },
    "2": {
      "orange": "It's orange."
    }
  },
  "message": "success retrieved fruits list",
  "status": 200
}

Now I am building a component that calls my API (successfully) and I am trying to create a list of the items within data.

class FruitList extends Component {

    constructor(props) {
        super(props);
        this.state = {
            fruits: {},
            error: null
        }
    }

    componentDidMount() {
        fruitsApi.getFruits()
            .then(response => response.json())
            .then(
                (results) => {
                    this.setState({
                        fruits: results.data,
                        error: false
                    });
                },
                (error) => {
                    this.setState({ error: true });
                    console.log(error);
                }
            )
    }

    getFruits() {
        const items = this.state.fruits.map(
            ([k, v]) => <li key={k}>{v}</li>
        );
    }

    render() {
        return (
            <Container>
                <Row>
                    <ul>
                        { this.getFruits() }
                    </ul>
                </Row>
            </Container>
        );
    }
}

export default FruitList

From this, I result in a TypeError: this.state.fruits.map is not a function. I have tried looking at other examples/errors similar, but can't seem to reason why my case is not acting properly. Any thoughts?

Thanks!

sgerbhctim
  • 3,420
  • 7
  • 38
  • 60
  • you cant map over an object. From your code , fruits is an object – joy08 Apr 28 '20 at 19:44
  • So how would one iterate over an object? It seems all the examples use `.map` – sgerbhctim Apr 28 '20 at 19:45
  • Well, the error is correct, there's no `map` function on `{ "1": { "apple": "Delicious" }, "2": { "orange": "It's orange." } }`... There would be if it was `[ { "apple": "Delicious" }, { "orange": "It's orange" }]` though... – Heretic Monkey Apr 28 '20 at 19:45
  • How would I pull out the `key`, `value` from this as in python `for k, v in fruits.items()` – sgerbhctim Apr 28 '20 at 19:46
  • 1
    [Converting JavaScript object with numeric keys into array](https://stackoverflow.com/q/20881213/215552) – Heretic Monkey Apr 28 '20 at 19:47
  • Does this answer your question? [Converting JavaScript object with numeric keys into array](https://stackoverflow.com/questions/20881213/converting-javascript-object-with-numeric-keys-into-array) – Heretic Monkey Apr 28 '20 at 19:50

3 Answers3

1

fruits is an object, not an array, the method map only exists natively on the array type in JavaScript.

To iterate over the key value pairs of fruits you can do either of the following:

getFruits() {
    const items = Object.keys(this.state.fruits).map(
        (key) => <li key={key}>{this.state.fruits[key]}</li>
    );
}

or

getFruits() {
    const items = Object.entries(this.state.fruits).map(
        ([key, value]) => <li key={key}>{value}</li>
    );
}
adrum
  • 71
  • 2
  • I included a `return items` statement.. Now, I reach this error: `Objects are not valid as a React child (found: object with keys {orange}). If you meant to render a collection of children, use an array instead.` – sgerbhctim Apr 28 '20 at 20:00
  • This is because in the example above your json has the keys `1` and `2` so when doing: `Object.entries(this.state.fruits).map(([key, value]) =>` the key will be `1` and the value will be `{ "apple": "Delicious" }` which is still an object, and react throws an error here. I'm not sure how you want to use your data but I think that if you're going to use an object structure for your api response it makes more sense to use this for `data`: `{ "apple": "delicious", "orange": "It's orange" }` vs maintaining indeces as object keys. Or use an actual array. – adrum Apr 28 '20 at 20:02
  • I want to hold integrity of the order, I know that this can be done in a list but for my use case holding the `1` is valuable. I am just putting together a dummified code example... how can I access the nested structure of `value` in the example above? – sgerbhctim Apr 28 '20 at 20:09
  • 1
    Arrays will hold the index of the order for you as well. If your data was: `[{ "apple": "Delicious" }, { "orange": "It's orange." }]` and you did `fruits.forEach((fruit, idx) => ...))` the second argument of for each, `idx` would give you either the 1 or the 2 and this makes it simpler to add / remove data since the array maintains the numbers for you. It's important to think of how to structure the data that you're sending to the frontend, not just how to access it once it's there. – adrum Apr 28 '20 at 20:17
  • Additionally, there's no easy answer to the how to access the value problem, since your keys aren't predictable - i.e. the key is the name of the fruit. If you structure your data like: `[{ "name": "apple", "description": "Delicious" }, { "name": "orange", "description": "It's orange." }]` then the keys are *serialized, i.e. predictable, and you can use the following: `fruits.forEach((fruit, idx) =>
  • {fruit.name}: {fruit.description}
  • ))` which is far cleaner, and which is what I'm guessing you're trying to do. – adrum Apr 28 '20 at 20:20