React.js warning when iterating a list with map() - reactjs

I got this warning from web debug console:
react-jsx-dev-runtime.development.js:87 Warning: Each child in a list should have a unique "key" prop.
Check the render method of App. See https://reactjs.org/link/warning-keys for more information. at div at App (http://localhost:3000/static/js/bundle.js:31:80)
Below is my code:
import './App.css';
import {ethers} from "ethers";
import { useState } from 'react';
function App() {
const [account, setAccount] = useState("")
const [data, setData] = useState([])
console.log(data);
const connect = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
let res = await provider.send("eth_requestAccounts", []);
setAccount(res[0]);
getData(res[0]);
}
const getData = (_account) => {
const options = {method: 'GET', headers: {accept: 'application/json'}};
fetch(
'https://api.opensea.io/api/v1/collections?asset_owner=0x3FB65FEEAB83bf60B0D1FfBC4217d2D97a35C8D4&offset=0&limit=3',
// `https://api.opensea.io/api/v1/collections?asset_owner=${_account}&offset=0&limit=3`,
options)
.then(response => response.json())
.then(response => {
setData(response);
console.log(response)})
.catch(err => console.error(err));
};
return (
<div className="App">
<button
onClick={connect}>
Connect
</button>
{data.map(nft => {
return(<div>
<img src={nft.featured_image_url} width="100px" height="100px"/>
<p>
{nft.discord_url}
</p>
<p>
{nft.banner_image_url}
</p>
</div>)
})}
<button
onClick={getData}>
GetData
</button>
</div>
);
}
export default App;
The code actually works as I expected. but when opening debug console from chrome I can see this warning pasted above.
Not sure why this warning? need some help, thank you
Googled this warning msg but cannot find useful info for this warning.
Is this warning a real issue or this can be ignored?

You need to add a key to your returned element, because React need to differentiate each elements.
You just need to add the parameter key to your block like:
{data.map(nft => (
<div key={nft.id}>
<img src={nft.featured_image_url} width="100px" height="100px"/>
<p>
{nft.discord_url}
</p>
<p>
{nft.banner_image_url}
</p>
</div>
))}
Why did I used nft.id ?
Most often, peoples use array map()'s index property as key, but it can be a bad practice depending on your goal.
using:
{data.map((nft, index) => (
<div key={index}>
...
))}
Works pretty fine, BUT in some cases (not that rare), when you perform edit action on your array's element, and you end up sorting them, React will be very confused.
Imagine you create an online music platform such as Spotify, and your API return you a list of your own playlist, ordered by name. The day you'll edit one playlist name, your entire playlist will have unwanted comportement because your element array's order will be modified, and the index you used as key.
So you may use map's index property as key, but be aware of what you need, it's generally better to use your element's id, uuid or other unique value as key.
See more on the official website

You must provide a unique key when you map with react.
Here is how :
{data.map((nft, index) => {
return(<div key={index}>
This is just an example. You can provide your own index.div key={nft} could work too if it is unique for each data.

Related

How to implement error boundaries in react with MERN?

My goal is to simply dynamically present the data from a mongo database of a specific document.
const Details = () => {
const { id } = useParams()
const [product, setProduct] = useState(null)
useEffect(() => {
const fetchProduct = async () => {
const response = await fetch(`/api/products/${id}`)
const json = await response.json()
if (response.ok) {
setProduct(json)
console.log(json)
}
}
fetchProduct()
}, [id])
this code works fine as it gets the product, but my problem is occurring with the rendering:
return (
<div className="details">
<Menu />
<h1 className="movement">Product Details - {product.name}</h1>
</div>
);
}
the error that I'm getting is Uncaught TypeError: Cannot read properties of null (reading 'name') and to Consider adding an error boundary to your tree to customize error handling behavior.
my question being is how do i implement correct error handling
Just use the optional chaining operator:
product?.name
It's typical that in the first render product is not yet available so you cannot access to any of its properties. With the optional chaining operator you are covering this case.
See more: https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Operators/Optional_chaining
If you want to add Error Boundaries:
https://reactjs.org/docs/error-boundaries.html#gatsby-focus-wrapper
React renders the component before you make an api request, thus product doesn't exist (it's null based on how you set its initial value). Then when response is received you set product to state and react re-renders your component. Now product exists.
To solve it, render h1 only when product exist.
<div className="details">
<Menu />
{ product && <h1 className="movement">Product Details - {product.name}</h1> }
</div>
Also, you can render a message if product is not yet exist
<div className="details">
<Menu />
{ product && <h1 className="movement">Product Details - {product.name}</h1> }
{ !product && <p>Loading ... </p>
</div>

Is there a way of creating as many react components as there are items in a API?

I would like to know if there is a way (and which one it is) of being able to render as many Card components as there are items in an API.
THis is the api url
https://6033c4d8843b15001793194e.mockapi.io/api/locations
I want to create a card for each item - not manually cause they might change.
How would I do that? I have googled and saw that I could maybe use mapping, but how do I fetch the number of ids there are and render as many cards as there are?
I have the card components and I am already able to fetch the names, id, etc...
function Card() {
const url = "https://6033c4d8843b15001793194e.mockapi.io/api/locations";
const [locations, setLocations] = useState(null);
useEffect(() => {
axios.get(url)
.then(response => {
setLocations(response.data)
})
}, [url])
if(locations) {
return (
<div>
<h1>Acme HQ</h1>
<p>{locations?.[0].name}</p>
</div>
)
}
return (
<div>
<h1>error</h1>
</div>
)
}
thank you.
You can use map() like this and send API information as props to component <Card/> than you style your Card component as u want.
{result.map((info) => (
<Card
create={info.createdAt}
name={info.name}
user={info.userCount}
description={info.description}
key={info.id}
/>
))}

Single responsibility in React component

I was learning Single responsibility principle in React and created such component:
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const SingleResponsibilityPrinciple = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
const showDetails = (userId) => {
const user = filteredUsers.find(user => user.id===userId);
alert(user.contact)
}
return <>
<div> Users List</div>
<div> Loading state: {isLoading? 'Loading': 'Success'}</div>
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(user.id)}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
</>
}
As you can see above, I have this code
const user = filteredUsers.find(user => user.id===userId);
The question is Is it best practice that if whenever we use map, reduce or any array function in React component, we should extract that logic out of a component, that is, filteredUsers.find(user => user.id===userId); should be extracted and we need to create utility function. So, function should not care about how a particular thing is done. Is it true?
Thank you for your question. I want to advice as follows
It is better for your to check if filteredUsers exists or not in your return.
{filteredUsers?.map(user=>{
//your code
})
You don't need to get specific user as you already loop in your map method. Just simply do something like this
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(alert(user.contact))}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
Remember the difference between find, filter method of Javascript array. If you have unique value according to userId, you simply use find method to get the first value not array, if you use filter, you get arrays of the condition. Are you sure you don't need to alert(user[0].contact), not alert(user.contact)?

React hooks component not updating after map, but see data loading / refreshing with error

I'm using React hooks and Typescript.
Basically, I'm grabbing data from an API through a service in useEffect, setting state through a .then(). When I log the data from this point, I can see it in the console, but the component is not updating. I'm an experienced dev but new to React completely.
Also, if I do something dumb like put {newsItems} in the return unwrapped once the data is loaded is will show an error. I'm guessing it is some simple mistake I am just blind to.
Here is the code:
import React, { Fragment, useContext, useEffect, useState } from 'react'
import { IFeedItem, IOwlerResponse, OwlerResponse } from '../../../../app/ models/owler'
import FeedItem from './FeedItem';
function formatOwlerResponse(response: any): OwlerResponse {
// console.log(response.feeds);
return { feeds: response.feeds, pagination_id: response.pagination_Id }
}
class ItemService {
getItems(url: string): Promise<OwlerResponse> {
return fetch(
`https://api.owler.com/v1/feed/url?domain=${url}&format=json&limit=100`,
{
method: "GET",
headers: new Headers({
(Redacted)
})
})
.then(res => res.json())
// .then(res => res.map((response: any) => formatOwlerResponse(response))
.then(res => formatOwlerResponse(res))
}}
export const CompanyNewsFeed = () => {
const [url, setUrl] = useState('google.com');
const [newsItems , setNewsItems] = useState<IFeedItem[]>([])
const [isLoading, setIsLoading] = useState(true);
const client = new ItemService()
useEffect(()=>{
client.getItems('google.com').then(r=> {
console.log(r.feeds)
setNewsItems(r.feeds);
})
console.log('set')
}, [url, formatOwlerResponse]);
return (
<Fragment>
{newsItems.map(item =>{
<Fragment key={item.id}>
<h1>{item.title}</h1>
<p>{item.description}</p>
</Fragment>
})}
</Fragment>
)
}
export default CompanyNewsFeed
Update -> I made a button for forceUpdate() and refresh() when I hit those I got an error that said I might have more than one version of react installed, which would account for the odd behaviour, since I've seen tutorials mostly mapped out exactly like my application. I did have a moment where I was changing the dependences to take care of warnings and I was at various points on 16.8, 16.14, and 17.0.1 or 17.1. I had had Mobx in too, and I spent a day checking that that was setup correctly. Will update if that doesn't solve it.
Just need to consider these
first you're adding [url, formatOwlerResponse] as useEffect dependency array so if none of the variables don't change so your api will only be called once.
another thing that you need to consider is that to check for newsItems length because your mapping through it and if it's not an array it would give you an error
return (
<Fragment>
{ newsItems?.map(item =>{
<Fragment key={item.id}>
<h1>{item.title}</h1>
<p>{item.description}</p>
</Fragment>
})}
</Fragment>
)
So the mistake here was actually none of the above. Showed this to a friend of mine in an interactive sandbox, within 10 minutes he saw that this:
{newsItems.map(item =>{
<Fragment key={item.id}>
<h1>{item.title}</h1>
<p>{item.description}</p>
</Fragment>
})}
Should be
{newsItems.map(item =>(
<Fragment key={item.id}>
<h1>{item.title}</h1>
<p>{item.description}</p>
</Fragment>
))}
Can't believe I didn't see it.

Needs Help To Troubleshoot Fetching Single Document From Firebase Database As Detailed Page

I'm try to get single document as detail information from Firebase database under collection "books", however my array method map does not recognize as function due to the render produce "undefined". Somehow render again and produce the object value in log. I posted the screenshot of the log above, hoping somebody help me out, thanks!!!!!
import React, {useState, useEffect} from 'react'
import {Link} from 'react-router-dom'
import firebase from '../config/fbConfig'
const BookDetails = (props) => {
const [books, setBooks] = useState([])
useEffect(() => {
const db = firebase.firestore()
const id = props.match.params.id
var docRef = db.collection("books").doc(id);
docRef.get().then(doc => {
if(doc.exists){
const data = doc.data()
console.log("Document data:", data)
setBooks(data)
}else {
console.log("No such document!");
}
}).catch(error => {
console.log("Error getting document:", error);
})
}, [])
console.log('this log is before return', books.title)
return (
<div className="book_details">
<Link to="/"><h2>Home</h2></Link>
{console.log("this log is in the return method", books.title)}
<h1>The Summary Of the Book </h1>
{books.map( book => <ul key = "book.id" >
<li>Book Title: {book.title}</li>
<li>Book Author: {book.author}</li>
<li>Book Summery: {book.brief}</li>
</ul>)}
</div>
)
}
export default BookDetails
Because you are testing whether books is undefined and only call the map function if it is defined (i.e. {books && books.map( [...] )}), the problem must lie somewhere else.
You are fetching a single document from your Firebase database. Therefore, the returned data will not be an array but an object which does not have the map function in its prototype. You can verify this from your console logs.
Your component renders twice because you are changing its state inside the useEffect via setBooks(data).
const db = firebase.firestore()
const id = props.match.params.id
First of all move these lines inside of useEffect.
Coming to the problem
You are fetching a single doc(object) from firebase and saving it in a state which is an array. Change your useState to
const \[book, setBook\] = useState(undefined) // or useState({})
Change your return to
return (
<div className="book_details">
<Link to="/"><h2>Home</h2></Link>
{console.log("this log is in the return method", books.title)}
<h1>The Summary Of the Book </h1>
{book && <div key={book.id}> {book.brief} </div>}
</div>
)
// or {Object.keys(book).length !== 0 && <div key={book.id}> {book.brief} </div>}
if you have used empty object in useState.

Resources