I have React component :
import React from "react";
import { Pokemons } from "../Pokemons";
class Main extends React.Component {
state = {
pokemons: [],
};
componentDidMount() {
fetch("https://pokeapi.co/api/v2/pokemon?limit=20")
.then((responce) => responce.json())
.then((data) => this.setState({ pokemons: Object.values(data) }));
}
render() {
return (
<main className="container content">
<Pokemons pokemon={this.state.pokemons} />
</main>
);
}
}
export { Main };
The API I'm using is PokeAPI
My task is to display data about the first 20 Pokémon (which is what I'm trying to do in fetch() ). But I don't get exactly what I need:
Console information.png
That is, JSON with four elements comes in response, the one I need is in the last one ( 3: ):
Console information 2.png
**In addition, each of the twenty objects (Pokémon) has a name and a link: **
Console Information 3.png
From this I need only the name, the rest of the necessary information (type, height, sprite) are data on these links. Example data in the link:
Data from link.png
In fact, the name of the pokemon is also in the data contained in these links.
I have never come across such an API structure before =(
Question - Help me get the name, type, sprite, height and weight of the Pokémon...
get rid of Object.values(data)
replace it with data.results
data contains the first layer of data, which includes count, next, results, etc...
you are interested in the results object, that contains the name, type, etc...
by doing data.results, you push the 20 pokemon objects to your state, then you can either
iterate over the array of objects and render <Pokemon /> for each pokemon
or
send the entire pokemon array to <Pokemon /> component and do that within the component itself.
example:
return (
<main className="container content">
{this.state.pokemons.map((pokemon) => (
<Pokemons pokemon={pokemon} />)}
</main>
);```
return (
<main className="container content">
<Pokemons pokemons={this.state.pokemons} />
</main>
);```
// Pokemons Component accepts pokemon array, and iterate it
Related
I have a nested object that is usually rendered when I have finally transferred the state through navigation (transferring the product items to the payment page).
This question is a continuation from my previous question for the logic of my shopping cart functionality:
How to transfer product items to the payment page after clicking the checkout button in React?
Main Goal: I have to access the content values of the nested object and transform these values into a much more readable form by displaying the individual contents of them. I have to access the individual value of totalPrice and access the values of the inner object cartItems
Problem: Even after using Object.keys(obj).map((key) => () functionality, it doesn't display the individual values of the nested object such as the totalPrice and the inner object cartItems. Currently, I only did a JSON.stringify(obj) to display its nested object form so I can see it temporarily.
Nested object appearance currently:.
Goal appearance:
Source code from PaymentPage.jsx (where I did the Object.keys functionality):
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route,
useNavigate,
useLocation
} from "react-router-dom";
export default function PaymentPage(props) {
const navigate = useNavigate();
const location = useLocation();
const navigateToHomeOrderPage = () => {
navigate("/");
};
const data = location.state; //the passed object being used
return (
<aside className="block col-1">
<button
sx={{ width: 10 }}
style={{ maxWidth: "60px" }}
onClick={navigateToHomeOrderPage}
>
Go back
</button>
<h2>PAYMENT PAGE</h2>
{/* {JSON.stringify(data)} */}
{/* Temporary object display: */}
<pre sx={{ ml: 120 }}>{JSON.stringify(data)}</pre>
{/* {Object.keys(data).map((key) => (
<h3>
{key}: {data[key]}
</h3>
))}
ERROR: Objects are not valid as a React child (found: object with keys {id, name, price, image, qty}). If you meant to render a collection of children, use an array instead.
*/}
{/*
{Object.keys(data).map((keys) => {
return <h1>{data[keys].name}</h1>;
})}
No errors but notihng is displayed
*/}
</aside>
);
}
Full functioning App in CodeSandbox:
https://codesandbox.io/s/react-access-nested-object-forked-5xvp2x?file=/src/components/PaymentPage.jsx
Hoping for your guides and responses on this one since I am very much confused on how to access nested objects in React. Your guides, responses, and tweaks would indeed help me a lot on this one in gaining the logic out of it.
If your data object structure its going to be the same you can do it like this example:
{data?.cartItems.map((item ,index) =>
<div key={index} style={{display:'flex', gap:'8px '}}>
<img src={item.image} style={{height:'100px', width:'100px'}} />
<p>{item.name}</p>
<p>{item.price}</p>
<p>{item.qty}</p>
</div>
)}
TOTAL: {data?.totalPrice}
I have a similar situation like the one in the sandbox.
https://codesandbox.io/s/react-typescript-fs0em
Basically what I want to achieve is that Table.tsx is my base component and App component is acting like a wrapper component. I am returning the JSX from the wrapper file.
Everything is fine but the problem is whenever I hover over any name, getData() is called and that is too much rerendering. Here it is a simple example but in my case, in real, the records are more.
Basically Table is a generic component which can be used by any other component and the data to be displayed in can vary. For e.g. rn App is returning name and image. Some other component can use the Table.tsx component to display name, email, and address. Think of App component as a wrapper.
How can I avoid this getData() to not to be called again and again on hover?
Can I use useMemo or what approach should I use to avoid this?
Please help
Every time you update the "hover" index state in Table.jsx it rerenders, i.e. the entire table it mapped again. This also is regenerating the table row JSX each time, thus why you see the log "getData called!" so much.
You've effectively created your own list renderer, and getData is your "renderRow" function. IMO Table shouldn't have any state and the component being rendered via getData should handle its own hover state.
Create some "row component", i.e. the thing you want to render per element in the data array, that handles it's own hover state, so when updating state it only rerenders itself.
const RowComponent = ({ index, name }) => {
const [hov, setHov] = useState();
return (
<div
key={name}
onMouseEnter={() => setHov(index)}
onMouseLeave={() => setHov(undefined)}
style={{ display: "flex", justifyContent: "space-around" }}
>
<div> {name} </div>
<div>
<img
src={hov === index ? img2 : img1}
height="30px"
width="30px"
alt=""
/>
</div>
</div>
);
};
Table.jsx should now only take a data prop and a callback function to render a specific element, getData.
interface Props {
data: string[];
getData: () => JSX.Element;
}
export const Table: React.FC<Props> = ({ data, getData }) => {
return (
<div>
{data.map((name: string, index: number) => getData(name, index))}
</div>
);
};
App
function App() {
const data = ["Pete", "Peter", "John", "Micheal", "Moss", "Abi"];
const getData = (name: string, index: number, hov: number) => {
console.log("getData called!", index);
return <RowComponent name={name} index={index} />;
};
return <Table data={data} getData={getData} />;
}
This question already has answers here:
Understanding unique keys for array children in React.js
(27 answers)
Closed 2 years ago.
I am calling an API endpoint, saving it's data to a state and then rendering it. It's displaying in the browser but there is a warning on the console: Warning: Each child in a list should have a unique "key" prop..
My app.js:
class App extends Component {
render () {
return (
<div>
<Profile profiles={this.state.profile} />
</div>
)
}
state = {
profile: []
};
componentDidMount() {
fetch('http://127.0.0.1:8000/profiles')
.then(res => res.json())
.then((data) => {
this.setState({ profile : data })
})
.catch(console.log)
}
}
export default App;
I don't understand where do I put the key prop in render(). This is my snippet profile.js:
const Profile = ({ profiles }) => {
return (
<div>
<center><h1>Profiles List</h1></center>
{profiles.map((profile) => (
<div className="card">
<div className="card-body">
<h5 className="card-title">{profile.first_name} {profile.last_name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{profile.dob}</h6>
<p className="card-text">{profile.sex}</p>
</div>
</div>
))};
</div>
)
};
export default Profile;
What improvement do the key prop brings over not using it? I am getting overwhelmed with these <div>...</div> tags.
If you use map in your JSX return then you need to provide the parent element with the key prop so that it's uniquely identified.
https://reactjs.org/docs/lists-and-keys.html
You would preferably use an object ID but if you know a field (or combinations of fields) that would constitute a unique key then you could use that instead:
{profiles.map((profile) => (
<div
key={'profileList_'+profile.first_name+profile.last_name}
className="card"
>
...
</div>
)};
NOTE: In the example I used profileList_ as a prefix just in case you need to use the same unique identifier (object ID or in this case profile.list_name+profile.last_name) as a key somewhere else in a different context.
you have to set uniq value to key props to the first div inside map
{profiles.map((profile) => (
<div key={profile.id} className="card">
read the doc
I am using axios to get the data from an API. My component has an articles array in the state which is where the data from the API request will go.
I am still really new to React and trying to find information on displaying multiple articles on the page. That is what I am trying to figure out. Maybe I am using this.state wrong?
I've also tried fetch() then using a .then() to return the information. Neither way has helped me achieve what I am trying to display:
import React from "react";
import axios from "axios";
class Pages extends React.Component{
constructor(){
super();
this.state = {
articles : []
}
}
componentDidMount(){
this.getArticles().then(res =>
{
console.log(res);
this.setState({articles : res.data.articles});
});
}
getArticles(){
return axios.get('https://newsapi.org/v2/everything?domains=wsj.com,nytimes.com&apiKey=API_KEY');
};
render(){
return (
<div className="ui raised very padded text container segment">
<p>
Yo!
{this.state.articles[0] ? this.state.articles[0].author : ''}
</p>
</div>
);
}
}
export default Pages;
In the return section I want to display the article name, author, url link, and the content of the article.
If I understand correctly, you're wanting to render all articles that are available after your axios request completes.
To achieve that, consider using the map() method on the state.articles array. This allows you to "map" each item from the raw document JSON data to an <li> element containing the article information that you want to display to your users.
When rendering each <li>, you can access the content of the current article being mapped, to render that into the components overall rendered result:
render(){
return (
<div className="ui raised very padded text container segment">
<ul>
{ this.state.articles.map((article, index) => {
return (<li key={index}>
<h2>{ article.name }</h2>
<div>{ article.author }</div>
<p>{ article.content }</p>
<a href={article.url}>{ article.url }</a>
</li> )
})}
</ul>
<p>Yo!</p>
</div>
);
}
Something also to note is that, when rendering lists in React, you should include a unique key for each list item rendered (in the code above, the key is specified as the index of the article being mapped).
Update
To render the first article only (if present) while retaining your component's current state structure, you could slice() the articles array before performing the map(). This would cause a new array (with only the first article) to be passed to map() - the end result being that only the first article is rendered:
{ this.state.articles.slice(0,1).map((article, index) => {
/* Existing code from answer */
}}
So i'm trying to filter my star wars api data through a searchbox and got this so far :
class Card extends Component {
constructor(){
super()
this.state = {
jedi: [],
searchfield: ''
}
}
// Loop through API
componentDidMount(){
fetch('https://swapi.co/api/people/1')
.then(response => { return response.json()})
.then(people => this.setState({jedi:people}))
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value})
console.log(event.target.value)
}
render() {
const {jedi, searchfield} = this.state;
const filteredCharacters = jedi.filter(jedi => {
return jedi.name.toLowerCase().includes(searchfield.toLowerCase());
})
}
}
This is my SearchBox component
import React from 'react';
const SearchBox = ({searchfield, searchChange})=> {
return (
<div className= 'searchbox1'>
<input className = 'searchbox2'
type = 'search'
placeholder = 'search character'
onChange = {searchChange}
/>
</div>
)
}
export {SearchBox};
This is the render in the main app component
render() {
return (
<div className="App">
<header className="App-header">
<img src={lightsaber} className="App-logo" alt="logo"/>
<h1 className="App-title">
Star Wars Character App w/API
</h1>
</header>
<SearchBox searchChange= {this.onSearchChange} />
<div className = 'allcards'>
<Card jedi = {this.filteredCharacters}/>
</div>
</div>
);
}
It keeps giving me the error "jedi.filter is not a function'. Initially I figured since filter only works on Arrays and my data were strings, that i'd use jedi.split('').filter. But that didnt seem to work as I just got "jedi.split isnt a function". What's going on?
After running the code this link 'https://swapi.co/api/people/1' returns a single object (Luke Skywalker). You can't use .filter() on an object it is meant for arrays of data.
{name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …}
When I change the url 'https://swapi.co/api/people/' and remove the 1 I receive an object with another array of objects inside results.
I am assuming you want to search through a list of Jedis. If you want the list returned in the API you have to dig a step further into this object inside results. So you need to refactor your fetch as follows:
.then(people => this.setState({ jedi:people.results }))
This returns 10 objects inside of an array.
Once you have an array of these objects you can use the lodash library to filter through. npm i lodash and require it in as import _ from 'lodash'.
Then in your render right after your destructure you can run the following filter.
const filtered = _.filter(jedi, (item) => {
return item.name.indexOf(searchfield) > -1
})
console.log(filtered)
I am assuming after this you will be returning the list of filtered Jedis to the UI
I hardcoded 'Luke' in my search field state and it returned 1 Jedi successfully