I’m relative new to React and I am attempting to take a sloppily written component and make it more semantic by separating logic from the UI i.e. a dumb component and a smart component rather than both jumbled together.
Here is the sloppy component I’m working with:
import React, { Component } from 'react';
import './App.css';
import axios from "axios";
class CoinList extends Component {
constructor(props) {
super(props);
this.state = {
coinList: []
};
}
componentDidMount() {
axios.get(`https://min-api.cryptocompare.com/data/all/coinlist`)
.then(res => {
const coins = res.data;
this.setState({ coinList: coins});
});
}
// Object.keys is used to map through the data. Can't map through the data without this because the data is not an array. Map can only be used on arrays.
render() {
const data = this.state.coinList.Data;
if (data == null) return null;
return (
<div className="App">
{Object.keys(data).map((key) => (
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th className="col-md-2">Coin</th>
<th className="col-md-2">Symbol</th>
<th className="col-md-2">Algorithm</th>
<th className="col-md-2">#</th>
</tr>
</thead>
<tbody>
<tr>
<td className="col-md-2">{data[key].CoinName}</td>
<td className="col-md-2">{key}</td>
<td className="col-md-2">{data[key].Algorithm}</td>
<td className="col-md-2">{data[key].SortOrder}</td>
</tr>
</tbody>
</table>
</div>
))}
</div>
);
}
}
export default CoinList;
I have attempted to separate concerns by taking this code and breaking it up into two separate components. Here is what I have thus far:
I am getting an error in CoinListItem that says “Key is not defined”. Can anyone tell me what the problem is and how to properly separate this component into two components that are more semantic?
Correct me if I am wrong, however, isn’t key not supposed to be used for render data? I thought it’s specialization is for indexing virtual dom elements to help with re-renders for components that change?
Honestly I’m confused about this as well. I was trying to look at other projects I’ve worked along with in tutorials and emulate how they separate logic from UI in my project. I don’t fully understand it.
When you map through an array to make DOM elements, React requires you to give them a key. This is an invisible helper for it to know when and what to rerender. You are not meant to use that key inside the element, it is for React only. If you want to use it, you can also pass the same number in as another prop.
What does the data you get back look like? You’re saying coinList is an array, then you try to iterate through coinList.Data, which suggests it isn’t an array but an object with an array called Data as one of the fields.
Anyway, make the request, set the state in componentDidMount. You’ve got that. But then you need to pass that data as a prop to the child. Like
When you map through an array to make DOM elements, React requires you to give them a key. This is an invisible helper for it to know when and what to rerender. You are not meant to use that key inside the element, it is for React only. If you want to use it, you can also pass the same number in as another prop.
This is what I was trying to say. Key isn’t really a prop that you can use. It’s more metadata just for React to help with virtual dom changes.
@Nicknyr — have you changed your CoinListItem component at all and what error messages are you getting (if any)? I noticed that the component in question isn’t quite right (see comments below):
const CoinListItem = (props) => {
const data = this.state.coinList.Data;
// This component doesn't have its own state, this would actually, if
// I'm not mistaken, throw an error and stops things from being rendered
if (data == null) return null;
return (
<tr>
<td className="col-md-2">{props.data[key].CoinName}</td>
<td className="col-md-2">{props.key}</td>
<td className="col-md-2">{props.data[key].Algorithm}</td>
<td className="col-md-2">{props.data[key].SortOrder}</td>
</tr>
);
};
export default CoinListItem;
A better way to structure this (in my opinion) is to simply pass in the data for each coin into CoinListItem:
I didn’t check if it worked, sorry, I was just using it as a rough example. But the point stands: that’s how you do it - you pass via props. You have to do that, that’s the core concept of React. I think you’re also getting confused over the actual Data key and the concept of a key when iterating over arrays in React. This should work, not tested but correct data structure now:
So have a parent component that has state (array of coins). Use componentDidMount to make request and load. Use Object.values (or iterate using keys, values is much easier) to get the get the values in Data in your array of objects. Pass that array as a prop to a child (the actual rows containing the data). map over that array in the subcomponent. Use some value that you know will be unique for each mapped component as the key prop otherwise you’ll get a key warning in the console
This puts the core state in one place; try not to use it more than that, then you can just use props and stateless components.