react await async calling too soon giving undefined then the right result - javascript

I'm using async and await, however the result it calling two sets of times, the first set returns the empty default from the useEffect, and then 2nd time returns the correct data from the call.
This is leading to the data not rendering in the component.
import { Grid } from '#material-ui/core';
import React, { useEffect, useState } from 'react';
import { useEasybase } from 'easybase-react';
export default function Data() {
const [easybaseData, setEasybaseData] = useState([]);
const { db } = useEasybase();
const mounted = async() => {
const ebData = await db("cars").return().limit(1).all();
setEasybaseData(ebData);
};
useEffect(() => {
mounted();
}, []);
console.log('easybaseData');
const ez = easybaseData[0];
const i = ez?.image;
const n = ez?.name;
const p = ez?.price;
console.log(i);
return (
<Grid item xs={6} id={"outputs-grid"}>
<div className="image-result-wrapper">
<img
src={`http://localhost:3000/assets/images/${i}`}
className="image-result"
/>
</div>
<div className="details-result-wrapper">
<p className="details-result">{n}</p>
</div>
<div className="price-result-wrapper">
<p className="price-result">{`Evaluation: $${p}`}</p>
</div>
</Grid>
)
}
and the console.log:
What am I missing about this? All 3 data for image, name and price are returning undefined in inspector.

Related

React is rendering before api data arrived

i have an ojbect inside an object and i want to extract the sub object to map it but the component is readered before the data is arrived
the main object name is Movie and the sub object is Categories
and i want to map Categories but it says undefind.
import Card from '../UI/Card';
import classes from './MovieDetails.module.css';
import MovieCategoris from './MovieCategories';
import ReactPlayerProps from "react-player";
const MovieDetails = (props) => {
const Movie = props.Movie
const image = Movie.Image;
const Categories = Movie.Categories
const videoSrc = Movie.Video;
return (
<Card>
<div className={classes.VideoPlayer}>
<ReactPlayerProps
controls
volume={1}
muted={false}
width={"100%"}
height={"100%"}
url={videoSrc}
/>
</div>
<div className={classes.DetailsBox}>
<div className={classes.Details}>
<div className={classes.Title}>
<img src={image} />
</div>
<div className={classes.MovDet}>
<h3>{Movie.Title}</h3>
<ul>
<li>Duration: <label>{Movie.Duration}</label></li>
<li>Quality: <label>HD 720</label></li>
<li>release date: <label>{Movie.Year}</label></li>
<li>IMBb: <label>{Movie.Rate}</label></li>
</ul>
<h5>Categories</h5>
{/* <div>
<ul className={classes.Cat_li}>
{Categories.map((cat) =>
<li>{cat}</li>
)}
</ul>
</div> */}
</div>
<div className={classes.Desc}>
<p> {Movie.Description} </p>
</div>
</div>
</div>
</Card>
)
}
export default MovieDetails;
this is the function that get the api data and forward it with props to the component.
import MovieDetails from "../components/Content/MovieDetails";
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import FetchedMovie from '../Hooks/fetchSingleMovie'
const MovieDetailsPage = () => {
const [MovieData, setMovieData] = useState([])
const MovieId = useParams()
async function Movie(MovieId) {
const id = MovieId.movie
const result = await FetchedMovie(id)
setMovieData(result)
return result
}
useEffect(() => {
Movie(MovieId)
}, [])
return <MovieDetails Movie={MovieData} />
}
export default MovieDetailsPage;
this is the object i have on the api sever
That's because you ara passing an empty array in your MovieDetails component in MovieDetailsPage
you can render MovieDetails whene your data is ready
Try to change it in something like this
import MovieDetails from "../components/Content/MovieDetails";
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import FetchedMovie from '../Hooks/fetchSingleMovie'
const MovieDetailsPage = () => {
const [MovieData, setMovieData] = useState(null)
const MovieId = useParams()
async function Movie(MovieId) {
const id = MovieId.movie
const result = await FetchedMovie(id)
setMovieData(result)
return result
}
useEffect(() => {
Movie(MovieId)
}, [])
return MovieData && <MovieDetails Movie={MovieData} />
}
export default MovieDetailsPage;
it happens because the api will render after your first moviedetails call;
you can use an if before your map that everytime its defined, map the function like this: {categories && categories.map ... }; because everytime that the props changes page will rerender;

Getting undefined props in functional react components

How to pass the {requests} prop to the RequestRow component after executing the setRequests? My understanding is that the requests get initialized as undefined in the beginning and before being set with the asynchronously called object, it gets passed to the RequestRow component as undefined, and the error occurs.
import React, { useState, useEffect } from 'react';
import 'semantic-ui-css/semantic.min.css';
import Layout from '../../../components/Layout';
import { Button } from 'semantic-ui-react';
import { Link } from '../../../routes';
import Campaign from '../../../blockchain/campaign';
import { Table } from 'semantic-ui-react';
import RequestRow from '../../../components/RequestRow';
const RequestsIndex = ({ address }) => {
const { Header, Row, HeaderCell, Body } = Table;
const campaign = Campaign(address);
const [requestCount, setRequestCount] = useState();
const [requests, setRequests] = useState([]);
const getRequests = async () => {
const count = await campaign.methods.getRequestsCount().call();
setRequestCount(count);
};
let r;
const req = async () => {
r = await Promise.all(
Array(parseInt(requestCount))
.fill()
.map((_element, index) => {
return campaign.methods.requests(index).call();
})
);
setRequests(r);
};
useEffect(() => {
getRequests();
if (requestCount) {
req();
}
}, [requestCount]);
return (
<Layout>
<h3>Requests List.</h3>
<Link route={`/campaigns/${address}/requests/new`}>
<a>
<Button primary>Add Request</Button>
</a>
</Link>
<Table>
<Header>
<Row>
<HeaderCell>ID</HeaderCell>
<HeaderCell>Description</HeaderCell>
<HeaderCell>Amount</HeaderCell>
<HeaderCell>Recipient</HeaderCell>
<HeaderCell>Approval Count</HeaderCell>
<HeaderCell>Approve</HeaderCell>
<HeaderCell>Finalize</HeaderCell>
</Row>
</Header>
<Body>
<Row>
<RequestRow requests={requests}></RequestRow>
</Row>
</Body>
</Table>
</Layout>
);
};
export async function getServerSideProps(context) {
const address = context.query.address;
return {
props: { address },
};
}
export default RequestsIndex;
The RequestRow component is shown below. It takes in the {requests} props, which unfortunately is undefined.
const RequestRow = ({ requests }) => {
return requests.map((request, index) => {
return (
<>
<div>Request!!!</div>
</>
);
});
};
export default RequestRow;
The snapshot of the error is shown below:
I think React is trying to render your component before your promises resolve. If that's the case, all you need to do is set a default value (an empty array in your case) for your requests.
const [requests, setRequests] = useState([]);
May the force be with you.

logging the data but not rendering p tag , why?

I am using firebase firestore and i fetched the data , everything is working fine but when i am passing it to some component only one item gets passed but log shows all the elements correctly.
I have just started learning react , any help is appreciated.
import React, { useEffect, useState } from 'react'
import { auth, provider, db } from './firebase';
import DataCard from './DataCard'
function Explore() {
const [equipmentList, setEquipments] = useState([]);
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
data.docs.forEach(item => {
setEquipments([...equipmentList, item.data()]);
})
}
useEffect(() => {
fetchData();
}, [])
equipmentList.forEach(item => {
//console.log(item.description);
})
const dataJSX =
<>
{
equipmentList.map(eq => (
<div key={eq.uid}>
{console.log(eq.equipment)}
<p>{eq.equipment}</p>
</div>
))
}
</>
return (
<>
{dataJSX}
</>
)
}
export default Explore
You have problems with setting fetched data into the state.
You need to call setEquipments once when data is prepared because you always erase it with an initial array plus an item from forEach.
The right code for setting equipment is
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
setEquipments(data.docs.map(item => item.data()))
}

How to create a function in Reactjs that is not called by an event Listener?

I'm a beginner in reactjs and am trying to create a simple quiz app. What I want to do can be seen in the selectionQues() function.
import React,{useState,useEffect} from 'react';
import './App.css';
import Question from './components/Question';
function App() {
const [ques, setques] = useState([]);
const [currentQues, setcurrentQues] = useState([]);
//importing ques using api
useEffect(() =>{
fetch('https://opentdb.com/api.php?amount=20&category=18&difficulty=medium&type=multiple')
.then((res)=> res.json())
.then((question) => {
setques(question.results);
});
},[setques])
//selecting 5 ques at random for our quiz
const selectingQues = () => {
let curr=[];
let qlen=ques.length;
for(let i=0;i<5;i++){
let selector= Math.floor(Math.random()*qlen);
curr[i]=ques[selector];
}
setcurrentQues(curr);
// console.log(ques);
}
return (
<div className="App">
<Question currentQues={currentQues}/>
</div>
);
}
export default App;
Now what I want to do is call this SelectingQues() without explicitly using a onClick listener or anything of the like. Is this possible using useEffect? But i want it to be executed after the first useEffect has been called.
Question component is not attached since rn it does nothing more than just displaying the ques.
IDEA: Main Idea is to get our Questions array in our API call and make sure to get random 5 questions there itself. How I do this is, as soon as I am receiving my data from my API call, I do a promise chaining and handle all my code for get 5 random questions in this another then() block. As I receive my 5 random questions, I saved in the state currentQues.
Codesandbox Demo
import React, { useState, useEffect } from "react";
import "./App.css";
function App() {
const [currentQues, setcurrentQues] = useState([]);
//importing ques using api
useEffect(() => {
selectingQues();
}, []);
// selecting 5 ques at random for our quiz
const selectingQues = async () => {
const response = await fetch(
"https://opentdb.com/api.php?amount=20&category=18&difficulty=medium&type=multiple"
);
const data = await response.json();
console.log(data);
const initialQuestions = data.results;
let curr = [];
// console.log(initialQuestions.length);
let length = initialQuestions.length;
for (let i = 0; i < 5; i++) {
let selector = Math.floor(Math.random() * length);
curr[i] = initialQuestions[selector];
}
setcurrentQues(curr);
};
return (
<div className="App">
{currentQues.length > 0 && <Question currentQuestions={currentQues} />}
</div>
);
}
export default App;
const Question = ({ currentQuestions }) => {
// const { question, correct_answer } = question;
console.log(currentQuestions);
return (
<>
{currentQuestions.map((question) => (
<div key={question.question}>
<p>
<strong>Question:</strong> {question.question}
</p>
<p>
<strong>Answer:</strong> {question["correct_answer"]}
</p>
</div>
))}
</>
);
};

How to remove an element from an array dynamically? React.js

There are two components, I want to implement an element array using the useContext hook, but when the button is clicked, the element is not removed, but on the contrary, there are more of them. Tell me what is wrong here. I would be very grateful!
First component:
import React from 'react';
import CartItem from './CartItem';
import Context from '../Context';
function Cart() {
let sum = 0;
let arrPrice = [];
let [products, setProducts] = React.useState([]);
let loacalProsucts = JSON.parse(localStorage.getItem('products'));
if(loacalProsucts === null) {
return(
<div className="EmptyCart">
<h1>Cart is empty</h1>
</div>
)
} else {
{loacalProsucts.map(item => products.push(item))}
{loacalProsucts.map(item => arrPrice.push(JSON.parse(item.total)))}
}
for(let i in arrPrice) {
sum += arrPrice[i];
}
function removeItem(id) {
setProducts(
products.filter(item => item.id !== id)
)
}
return(
<Context.Provider value={{removeItem}}>
<div className="Cart">
<h1>Your purchases:</h1>
<CartItem products = {products} />
<h1>Total: {sum}$</h1>
</div>
</Context.Provider>
)
}
Second component:
import React, { useContext } from 'react';
import Context from '../Context';
function CartList({products}) {
const {removeItem} = useContext(Context);
return(
<div className="CartList">
<img src={products.image} />
<h2>{products.name}</h2>
<h3 className="CartInfo">{products.kg}kg.</h3>
<h2 className="CartInfo">{products.total}$</h2>
<button className="CartInfo" onClick={() => removeItem(products.id)}>×</button>
</div>
);
}
export default CartList;
Component with a context:
import React from 'react';
const Context = React.createContext();
export default Context;
Adding to the comment above ^^
It's almost always a mistake to have initialization expressions inside your render loop (ie, outside of hooks). You'll also want to avoid mutating your local state, that's why useState returns a setter.
Totally untested:
function Cart() {
let [sum, setSum] = React.useState();
const loacalProsucts = useMemo(() => JSON.parse(localStorage.getItem('products')));
// Init products with local products if they exist
let [products, setProducts] = React.useState(loacalProsucts || []);
useEffect(() => {
// This is actually derived state so the whole thing
// could be replaced with
// const sum = products.reduce((a, c) => a + c?.total, 0);
setSum(products.reduce((a, c) => a + c?.total, 0));
}, [products]);
function removeItem(id) {
setProducts(
products.filter(item => item.id !== id)
)
}
...

Categories

Resources