I'm making an React application that involves fetching an API. The code is below, but basically I'm fetching the data from the API and then mapping over it to present information about each character. It works with the class component, but I tried to change it to a function component and it no longer works. There is no error, it simply doesn't display anything.
From using console.log(), I know that I'm successfully getting the information into the "characters" variable from the API when using the function component, and from comparing the two applications and using console.log() on "this.state.characters" (class) and "characters" (function), I'm pretty sure that getting the exact same data, an array of like 660 of the characters from the show where each character's information is inside of an object. When I tried to add paragraph tags with {characters[0].name} inside of the function component, I got the error "TypeError: Cannot read property 'name' of undefined".
I don't know if it's because I messed up something stupid or because I don't understand something about some detail about the difference between class and function components, either is obviously very possible. Thank you for any help.
Here is the code from the class component:
import React, { Component } from 'react';
export class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
characters: [],
nameInput: '',
locationInput: '',
loading: true
};
};
async componentDidMount() {
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
for (let i = 1; i < 34; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
this.setState({characters: array, loading: false}, () => console.log(this.state.characters));
}
readInput = (e) => {
this.setState({nameInput: e.target.value});
console.log(this.state.nameInput);
}
readLocationInput = (e) => {
this.setState({locationInput: e.target.value});
console.log(this.state.locationInput);
}
render() {
return (
<div className="all">
<h4>Search by name:</h4>
<input onChange={this.readInput} />
<h4>Search by location:</h4>
<input onChange={this.readLocationInput} />
<br />
<div className="row m-1">
{this.state.loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : this.state.characters.filter((item) => {
if (this.state.nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(this.state.nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (this.state.locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(this.state.locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square">
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
)
})}
</div>
</div>
);
}
};
Here is the code from the function component:
import React, { Component, useEffect, useState } from 'react';
import logo from '../rickandmortylogo.png';
export const Body = () => {
const [characters, setCharacters] = useState([]);
const [nameInput, setNameInput] = useState('');
const [locationInput, setLocationInput] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
const fetchAPI = async () => {
for (let i = 1; i < 34; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
}
fetchAPI();
setCharacters(array);
setLoading(false);
}, []);
const readInput = (e) => {
setNameInput(e.target.value);
console.log(nameInput);
}
const readLocationInput = (e) => {
setLocationInput(e.target.value);
console.log(locationInput);
}
return (
<>
<div className="text-center">
<img src={logo} className="img-fluid" />
</div>
<h2>Click on a character here to add them to your favorites. Choose "Check Favorites" in the menu bar to see your favorites and "Search Characters" to come back.</h2>
<div className="all">
<h4>Search by name:</h4>
<input onChange={readInput} />
<h4>Search by location:</h4>
<input onChange={readLocationInput} />
<br />
<div className="row m-1">
{loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : characters.filter((item) => {
if (nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square">
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
);
})}
</div>
</div>
</>
);
};