Rendering text after fetching data from database using React and AWS Amplify - reactjs

I am trying to use React and AWS Amplify to build a web app that will make a request to the database and display some text accordingly. However, I encounter the following error:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
The error message also indicates that the error is occurring after setNfts(nftList);
App.jsx:
function App() {
const [nfts, setNfts] = useState([]);
const fetchNfts = async () => {
try {
const nftData = await API.graphql(graphqlOperation(listNfts));
const nftList = nftData.data.listNfts.items;
console.log('nft list', nftList);
setNfts(nftList);
} catch (error) {
console.log('error on fetching nfts', error);
}
};
useEffect(() => {
fetchNfts();
}, []);
return (
<div className="App">
<header className="App-header">
<AmplifySignOut />
<h2>My App Content</h2>
</header>
<Box sx={{ display: 'grid', columnGap: 3, gridTemplateColumns: 'repeat(3, 1fr)' }}>
{nfts.map((nft, idx) => {
return (
<NftCard filePath={nft.filePath} name={nft.name} creator={nft.creator} idx={idx} />
);
})}
</Box>
</div>
);
}
NftCard.jsx:
import React from 'react';
import { Paper } from '#material-ui/core';
export function NftCard(filePath, name, creator, idx) {
return(
<Paper variant="outlined" elevation={2} key={`nft${idx}`}>
<div className="nftCard">
<div>
<img src={filePath} alt="" width="100px" height="100px" />
<div className="nftTitle">{name}</div>
<div className="nftCreator">{creator}</div>
</div>
</div>
</Paper>
);
}

console.log('nft list', nftList);
Does this have any data logged out?
And if so, can you please provide output?

You can try to send data to Component with:
<NftCard filePath={nft["0"].filePath} name={nft["0"].name} creator={nft["0"].creator} idx={idx} />
and Show the result of that

The props passed to NftCard should be listed separately and have curly braces around them:
function NftCard({ filePath, name, creatorThumbnail, creator, price, idx }) {
...
}

Related

In React 18, is useEffect's function guaranteed to run AFTER a Suspense promise completes?

I have a simple master-detail scenario where on the left side, I load a list of cities using useSwr with a REST service, then on the right side I have a city detail windows that also uses useSwr to load a single city (either from clicked on left, or first load).
In the code below, I'm calling the useEffect function, and then using the data retrieved from useSwr in a state setting call (setSelectedCityId).
This works, and there has always been data associated with the cities array, but I'm wondering if it is guaranteed that useEffect's function will run AFTER the Suspense promise is completed (like it seems to be).
Here is my simple code:
import { Suspense, useEffect, useState, useTransition } from "react";
import useSwr from "swr";
const fetcher = (...args) => fetch(...args).then((res) => res.json());
function CityDetailFallback() {
return <div>Loading (CityDetail)</div>;
}
function CityDetail({ selectedCityId }) {
function CityDetailUI({ selectedCityId }) {
const { data: city } = useSwr(
selectedCityId
? `http://localhost:3000/api/city/${selectedCityId}`
: null,
fetcher,
{
suspense: true,
}
);
if (!city) {
return <div>loading city...</div>
}
return (
<div className="row">
<div className="col-9">
<div>{JSON.stringify(city)} </div>
</div>
</div>
);
}
return (
<Suspense fallback={<CityDetailFallback />}>
<CityDetailUI selectedCityId={selectedCityId}></CityDetailUI>
</Suspense>
);
}
function CityList({
setSelectedCityId
}) {
//
const { data: cities } = useSwr("http://localhost:3000/api/city", fetcher, {
suspense: true,
});
useEffect(() => {
setSelectedCityId(cities[0].id);
}, []);
return (
<div className="col-3">
{cities.map((city) => {
return (
<div key={city.id}>
<button
onClick={(e) => {
setSelectedCityId(city.id);
}}
>
{city.city}
</button>
</div>
);
})}
</div>
);
}
export default function App() {
const [selectedCityId, setSelectedCityId] = useState();
return (
<div className="container">
Site Root
<hr />
<Suspense fallback={<div>Loading...</div>}>
<div className="row">
<div className="col-3">
<b>CITY LIST</b>
<hr />
<CityList
setSelectedCityId={setSelectedCityId}
selectedCityId={selectedCityId}
/>
</div>
<div className="col-9">
<div>
<b>CITY DETAIL (TOP ROW SELECTED AUTOMATICALLY)</b>
<hr />
<CityDetail selectedCityId={selectedCityId} />
</div>
</div>
</div>
</Suspense>
</div>
);
}
Note: I can't create a code sandbox because of a current bug in useSwr around suspense. https://github.com/vercel/swr/issues/1906 I'm testing currently with Create React App and using a dummy api endpoint for the REST calls. sorry :(
Yes, in React 18 useEffect always runs when the tree is consistent. So effects fire only after the tree is ready and not suspended.

Dynamically rendering child components in react

I'm using firestore database to store my data in the collection "listings". So for each document in "listings", I need to render a <BookListing/> element in Home.js with the data from each document. From my research, there are a few other questions similar to this one out there, but they're outdated and use different react syntax. Here's my code:
function BookListing({id, ISBN, title, image, price}) {
return (
<div className="bookListing">
<div className='bookListing_info'>
<p className="bookListing_infoTitle">{title}</p>
<p className="bookListing_infoISBN"><span className="bookListing_infoISBNtag">ISBN: </span>{ISBN}</p>
<p className="bookListing_infoPrice">
<small>$</small>
{price}
</p>
</div>
<img className="bookListing_img" src={image} alt=""></img>
<button className="bookListing_addToCart">Add to Cart</button>
</div>
)
}
export default BookListing
function Home() {
document.title ="Home";
useEffect(() => {
getDocs(collection(db, 'listings'))
.then(queryCollection => {
queryCollection.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
const element = <BookListing id="456" ISBN="0101" title="sample_title" image="https://nnpbeta.wustl.edu/img/bookCovers/genericBookCover.jpg" price="25"/>;
ReactDOM.render(
element,
document.getElementById('home-contents-main')
);
})
});
}, []);
return (
<div className="home">
<div className="home_container">
<div id="home-contents-main" className="home_contents">
</div>
</div>
</div>
)
}
export default Home
It's best (and most common) to separate the task into two: asynchronously fetching data (in your case from firestore), and mapping that data to React components which are to be displayed on the screen.
An example:
function Home() {
// A list of objects, each with `id` and `data` fields.
const [listings, setListings] = useState([]) // [] is the initial data.
// 1. Fetching the data
useEffect(() => {
getDocs(collection(db, 'listings'))
.then(queryCollection => {
const docs = [];
queryCollection.forEach((doc) => {
docs.push({
id: doc.id,
data: doc.data()
});
// Update the listings with the new data; this triggers a re-render
setListings(docs);
});
});
}, []);
// 2. Rendering the data
return (
<div className="home">
<div className="home_container">
<div className="home_contents">
{
listings.map(listing => (
<BookListing
id={listing.id}
ISBN={listing.data.ISBN}
title={listing.data.title}
image={listing.data.image}
price={listing.data.price}
/>
))
}
</div>
</div>
</div>
);
}
Some tips:
Fetching data from other web servers or services can be, and typically is, done in the same manner.
This example could be improved a lot in terms of elegance with modern JS syntax, I was trying to keep it simple.
In most cases, you don't want to use ReactDOM directly (only for the entry point of your app), or mess with the DOM manually; React handles this for you!
If you're not familiar with the useState hook, read Using the State Hook on React's documentation. It's important!
You can create a reusable component, and pass the data to it, and iterate over it using map() . define a state, and use it within the useEffect instead of creating elements and handling the process with the state as a data prop.
function BookListing({ id, ISBN, title, image, price }) {
return (
<div className="bookListing">
<div className="bookListing_info">
<p className="bookListing_infoTitle">{title}</p>
<p className="bookListing_infoISBN">
<span className="bookListing_infoISBNtag">ISBN: </span>
{ISBN}
</p>
<p className="bookListing_infoPrice">
<small>$</small>
{price}
</p>
</div>
<img className="bookListing_img" src={image} alt=""></img>
<button className="bookListing_addToCart">Add to Cart</button>
</div>
);
}
function Home() {
const [data, setData] = useState([]);
useEffect(() => {
document.title = 'College Reseller';
getDocs(collection(db, 'listings')).then((queryCollection) => setData(queryCollection));
}, []);
return (
<div className="home">
<div className="home_container">
<div id="home-contents-main" className="home_contents">
{data.map((doc) => (
<BookListing
id="456"
ISBN="0101"
title="sample_title"
image="https://nnpbeta.wustl.edu/img/bookCovers/genericBookCover.jpg"
price="25"
/>
))}
</div>
</div>
</div>
);
}
export default Home;

I am trying to integrate stripe frontend in my react and redux project

I am using react and redux in my project i already integrated complete backend in spring boot
and by following this tutorial i am trying to integrate frontend in react https://stripe.com/docs/stripe-js/react#elements-consumer
This is my frontend
export class RecurringSubscription extends Component {
static propTypes = {
client: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
};
render() {
const stripePromise = loadStripe('pk_test_123');
const CARD_ELEMENT_OPTIONS = {
style: {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4',
},
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a',
},
},
};
return (
<div className="client-recurring-subscription">
<main>
<section className="section" style={{ paddingTop: 20 }}>
<div className="container-fluid">
<div className="row justify-content-center">
<div className="col-12">
<div className="card card-info">
<div className="card-header">
<h3 className="card-title"> Payment</h3>
</div>
<div className="card-body">
<div className="row">
<div className="col-lg-6 col-md-8 col-12 my-auto mx-auto">
<Elements stripe={stripePromise}>
<MyCheckoutForm stripe={stripePromise} projectId={projectId} userId={userId} />
</Elements>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
</div>
);
}
}
function mapStateToProps(state) {
return {
client: state.client,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ ...actions }, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(RecurringSubscription);
This is MyCheckoutForm Component
class CheckoutForm {
handleSubmit = async () => {
const { stripe, elements,projectId,userId } = this.props;
console.log(stripe);
console.log(elements);
console.log(projectId); //udefined
console.log(userId); // unndefied
if (!stripe || !elements) {
// Stripe.js has not loaded yet. Make sure to disable
// form submission until Stripe.js has loaded.
return;
}
// Get a reference to a mounted CardElement. Elements knows how
// to find your CardElement because there can only ever be one of
// each type of element.
const cardElement = elements.getElement(CardElement);
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: 'card',
card: cardElement,
});
if (error) {
console.log('[error]', error);
} else {
console.log('[PaymentMethod]', paymentMethod);
}
};
render() {
const { stripe } = this.props;
console.log(stripe);
return (
<div>
<CardElement />
<br />
<button
className="btn-sm btn"
type="submit"
disabled={!stripe}
onClick={() => this.handleSubmit()}
>
Pay
</button>
</div>
);
}
}
export default function InjectedCheckoutForm = () => {
return (
<ElementsConsumer>
{({ elements, stripe }) => <CheckoutForm elements={elements} stripe={stripe} />}
</ElementsConsumer>
);
};
Now my issue
1- What is the role of InjectedCheckoutForm?
2- And How should i complete this integration ?
Whenever i click pay button element prop is undefined!!
The injected form is a way to separate the concern of bringing in the <ElementsConsumer> to provide stripe and elements to render your component.
In your case, you're exporting your CheckoutForm (rather than the InjectedCheckoutForm ) so your RecurringSubscription component is loading CheckoutForm without elements quite explicitly. It would be expected then that elements prop is undefined.
You should change your CheckoutForm file to instead export default InjectedCheckoutForm; at the end and see if that resolve your issue.

How do I get the component to redirect with a handleClick?

I am working on a ProductList component, that when you click on the div with the button in it, it should take you to the ProductDetail component and the url associated with it, but so far nothing has worked.
This is about my 10th different way of doing it and I honestly don't know what else to try at this point. My error message I am currently getting is 'upc is undefined' and before that it just returned 'undefined' as a part of the url. Any help would be much appreciated, thank you.
const ProductList = ({ result, upc }) => {
const history = useHistory()
const [itemId, setItemId] = useState('')
const containerStyles = {
height: '100vh',
overflow: 'auto',
textAlign: 'center',
padding: '5vh',
alignItems: 'center'
}
console.log(result)
const handleClick = (id) => e => {
e.preventDefault()
console.log(id)
history.push(`/productdetail/${result[id].upc}`)
}
return (
<Grid container direction='column'>
<div style={containerStyles}>
<div>
<button>
{!result
? (
<h1>No results found</h1>
)
: (
<div>
{result.map((item) => (
<p key={item.id} onClick={handleClick(item.id)}>
{item.name}
<img src={item.image_url} />
{item.upc}
</p>
))}
</div>
)}
</button>
</div>
</div>
</Grid>
)
}
export default ProductList
Please add on your app.js router the following prop
<Router history={history}>
....
</Router>

Warning on empty prop: why it happens?

I have component, which needs to fetch server data before rendering:
class BooksManage extends Component {
componentWillMount () {
const { dispatch, fetchManagePage } = this.props
dispatch(fetchManagePage())
}
render () {
const sections = this.props.books.map((section, index) => {
return (
<div className='row' key={index}>
<BookSectionContainer index={index} />
</div>
)
})
return (
<div className='row'>
<div className='col-md-8'>
{sections}
<BookPaginationContainer paginationClass='books' />
</div>
<div className='col-md-3'>
<div style={{position: 'fixed', width: 'inherit'}}>
<EmployeesListContainer />
</div>
</div>
</div>)
}
}
As you can see, it uses BookPaginationContainer, which expects several state parameters:
Warning: Failed prop type: The prop `currentPage` is marked as required in `Paginate`, but its value is `undefined`.
Etc.
Problem: as BookPaginationContainer is child of BooksManage, what I expect to achieve with componentWillMount() is that dispatching an action fetches state parameters which needed.
What actually happens:
action REQUEST_MANAGE_PAGE # 18:41:49.770
Warning: Failed prop type: The prop `currentPage` is marked as required in `Paginate`, but its value is `undefined`.
action RECEIVE_MANAGE_PAGE # 19:01:40.327
After all page renders correctly, but I'm concerned with how good is this.
Don't render components that need data until you have that data.
render() {
...
if(!this.props.requireData) {
return <div>Loading</div>;
}
return (
<div className='row'>
...

Resources