How do I access a numeric key in firebase realtime database? - javascript

I'm quite new to Reactjs and Firebase so I'd need help.
I need to retrieve the data from the "0","1","2" keys (i.e. "Price", "Title", etc.) like I do with the "user" one.
In this parent component (Dashboard.js) I connect to the database:
import { useState, useEffect } from "react";
import OrderedItems from "./OrderedItems";
import classes from "./Dashboard.module.css";
import dash from '../../assets/dash.jpg';
import { auth } from "../../firebase";
import { useHistory } from "react-router-dom";
const Dashboard = () => {
const [ord, setOrd] = useState([]);
const history = useHistory();
useEffect(() => {
const fetchLps = async () => {
const query = `${auth.currentUser.uid}/Orders.json`
const response = await fetch(
"https://beatles-app-default-rtdb.europe-west1.firebasedatabase.app/" + query
);
const responseData = await response.json();
console.log(responseData);
const loadedOrd = [];
for (const key in responseData) {
loadedOrd.push({
id: key,
orderedItems: responseData[key].orderedItems,
});
}
setOrd(loadedOrd);
// setIsLoading(false);
};
fetchLps().catch((err) => {
let errorMessage =
"Something went wrong on our server! Please try again later.";
alert(errorMessage);
history.replace("/");
});
}, [history]);
const OrdList = ord.map((things) => (
<OrderedItems
key={things.id}
id={things.id}
orderedItems={things.orderedItems}
/>
));
return (
<section className={classes.cont}>
<div className={classes.welcome}>
<h1 className={classes.h1}>Welcome to your Dashboard!</h1>
<h3 className={classes.h3}> You can now start to shop. Go to the Shop page, select your favorite albums and go to Cart
in order to submit your order.</h3><br/>
</div>
{OrdList}
<img src={dash} alt="" width="100%" />
</section>
);
};
export default Dashboard;
console.log(responseData) output:
I retrieve the data in this child component (OrderedItems.js) using this syntax {props.user.address} (for example in this case I get "street" which is correct):
const OrderedItems = (props) => {
return (
<section>
<div>
<p>{props.user.address}</p> // THIS WORKS
<p>{props.orderedItems.0.Price}</p> // THIS DOESN'T WORK BECAUSE OF THAT 0 WHICH IS THE NAME OF THE FIREBASE KEY
</div>
</section>
);
};
export default OrderedItems;
Now the problem is that I can't do it with the above mentioned keys as the following syntax is not allowed because of that 0 number {props.orderedItems.0.Price}.
What should I do? The database structure is created automatically as I'm entering orders of multiple items using the POST method. So I suppose that the solution would be either to use a proper syntax (which I don't know at the moment) in order to access the data or to populate the database in a different way (but I don't think it is possible as Firebase does it on its own).
Thank you in advance!

Related

Nextjs getStaticProps failing to read firebase firestore data

I am trying to read my data from firestore using getStaticProps, but somehow, it is rendering an empty page. I must say, when I use useEffect, the data renders, but the moment I change it to getStaticProps, it returns an empty page. This is how the code looks like at the moment
import { db } from '#/Firebase';
import { collection, getDocs } from "firebase/firestore";
const reference = collection(db, "students");
function Card(props) {
const { studentsData } = props
return (
<div>
<p>This is just a test</p>
{studentsData && studentsData.map(students => (
<div className="l">
<p>{students.name}</p>
</div>
))}
</div>
)
}
export const getStaticProps = async () => {
const students = await getDocs(reference);
const studentsData = students.docs.map(doc => ({id: doc.id, ...doc.data() }))
console.log(studentsData);
return {
props: { studentsData }
}
}
export default Card
I have followed the docs and multiple tutorial blogs online, but it is now working for me. How and where could I be going wrong?

How to pass the data input from one component into another component?

Introducing The Problem
I am beginner ReactJS learner developing a simple weather app using OpenWeather API. The app is designed to fetch data from two components: one that returns the current weather of the user input and another one that returns the weather forecast for the next 5 days.
When the city name is typed down into the input field, the following message appears on the console:
GET https://api.openweathermap.org/data/2.5/weather?q=undefined&units=metric&appid=${Api.key} 400 (Bad Request)
I do not know how to pass the data from Search Component into App Component. Seriously, I have tried a lot of alternatives but they have been unsuccessful. There are commented lines of code to show my last try so far.
(ignore ForecastWeather because this component is empty)
I know that all of you are quite busy folks, but I would appreciate the help in a respectful way. Even suggestions about what I have to study (e.g. callBack) are welcome. I've tried this already:
https://stackoverflow.com/questions/56943427/whether-to-save-form-input-to-state-in-onchange-or-onsubmit-in-react
https://sebhastian.com/react-onchange/
The code is forward below:
App.js
import React, { useState } from "react";
import { Api } from "./Api";
import {
Search,
CurrentWeather,
ForecastWeather,
Footer,
} from "./components/index";
import "./App.css";
function App() {
const [getCity, setGetCity] = useState();
const [weatherData, setWeatherData] = useState(null);
const [forecastData, setForecastData] = useState(null);
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
return (
<div className="App">
<Search
searchResultData={handleSearchLocation}
textPlaceholder="Search for a place..."
/>
{weatherData && <CurrentWeather resultData={weatherData} />}
<ForecastWeather resultData={forecastData} />
<Footer />
</div>
);
}
export default App;
Search.jsx
import React, { useState } from "react";
function Search({ textPlaceholder, searchResultData }) {
const [searchCity, setSearchCity] = useState("");
//const handlerOnChange = ( event, dataSearch ) => {
//setSearchCity(event.target.value);
//setSearchCity(dataSearch);
//searchResultData(dataSearch);
//};
return (
<div className="componentsBoxLayout">
<input
value={searchCity}
//onChange={handlerOnChange}
onChange={(event) => setSearchCity(event.target.value)}
onKeyDown={(event) => event.key === "Enter" && searchResultData(event)}
placeholder={textPlaceholder}
/>
</div>
);
}
export default Search;
CurrentWeather.jsx
import React from "react";
function CurrentWeather({ resultData }) {
return (
<div className="componentsBoxLayout">
<p>{resultData.name}</p>
</div>
);
}
export default CurrentWeather;
ForecastWeather.jsx (empty)
import React from 'react';
function ForecastWeather() {
return (
<div className="componentsBoxLayout">ForecastWeather</div>
)
}
export default ForecastWeather;
Api.js
const Api = {
url: "https://api.openweathermap.org/data/2.5",
key: "etcetc",
img: "https://openweathermap.org/img/wn",
};
export { Api };
Yippee-ki-yay
You can not use getCity in this function:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
getCity is defined on that function so it does not exist when you try to use it, unless you need getCity later for another component I would delete it becuase is redundant and do this:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${dataSearch}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${dataSearch}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
When you run searchResultData on the search component you send the city you are looking for. Remember that useState will trigger a re-render but a function that is already running before that will never get the new value of the state if the state changes

why useState is not updating data?

I am very new to react and after taking a course, I just wanted to do a project in react that I already did in Vue earlier just to practice React. But my useState is not updating the data that I want to list out.
The data from api is an array of Objects. End goal is to make a data table. So to be dynamic, I took the keys of the first object to take the column names. I used DataTable from 'react-data-table-component' for the data table. That didn't work. Now just to debug I thought to list out the column names but that didn't work either but it console logs.
I know this is something very basic and have searched lot of help in the internet and tried to solve who faced similar issue from lot of portals but nothing helped so far. Where am I doing wrong ?
import React, { useState, useEffect, Component } from 'react';
import axios from 'axios'
const tableData = () => {
const [tableData, setTableData] = useState([]);
const [columns, setColumns] = useState([])
useEffect(() => {
axios.get('http://localhost:4000/')
.then((res) => {
if (res.status === 200) {
if (res.data === 0) {
console.log("Something is wrong !!!");
} else {
const data = res.data['rows']
const columns = Object.keys(data[0])
console.log(columns)
setTableData(data)
setColumns(columns)
}
}
}).catch(error => {
console.log(error)
})
}, [])
return (
<div>
<ul>
{columns.map((columnName, index) => {
const {name} = columnName;
return (
<li key={index}>{ name }</li>
)
})}
</ul>
</div>
)
}
in react state, set state works on references.
sometime we need to change the state to the same reference that we changed the value of it.
so instead put the same reference as an argument, we need to set a copy of it,
because if we will set to the same state that already in the state, react can not see any different so it will not render again.
in your example, you didn't use the same reference for data, but for columns you used the same name of the state and the new columns.
example:
let referenceName = 'John';
const [name, setName] = useState(referenceName);
referenceName = 'Johnny'
setName(...referenceName )
I want to set the name of the state, but if i will NOT use the '...', react will not see any difference because i use the same reference.. so I need to put a copy od the refence for set a new reference.
check the shape of response data
here I simpliplified the useEffect by call that function out side
import React, { useState, useEffect, Component } from "react";
import axios from "axios";
const tableData = () => {
const [tableData, setTableData] = useState([]);
const [columns, setColumns] = useState([]);
const fetchDetails = () => {
axios
.get("http://localhost:4000/")
.then((res) => {
const data = res.data["rows"];
const columns = Object.keys(data[0]);
console.log(columns);
setTableData(data);
setColumns(columns);
})
.catch((error) => {
//dont need to check the status if any occurs it will come to this catch block
console.log(error.message);
});
};
useEffect(() => {
fetchDetails();
}, [columns]);
return (
<div>
<ul>
{columns.map((columnName, index) => {
const { name } = columnName;
return <li key={index}>{name}</li>;
})}
</ul>
</div>
);
};

react constantly calling the API when using .map to go through the list

i have setup a strapi API, and i am using react to consume that API (with Axios).
here's what the code look like inside App.js
import axios from "axios";
import React, {useEffect, useState} from "react";
import LineCard from "./components/Linecard"
function App() {
// don't mind the URL i will fix them later
const root = "http://localhost:1337"
const URL = 'http://localhost:1337/pick-up-lines'
// this is the "messed up" data from strapi
const [APIdata, setAPIdata] = useState([])
//this is the clean data
const [lines, setLines] = useState([])
// the array that i will be using later to "setLines" state
const linesFromApi = APIdata.map((line, index) => {
const profileImage = root + line.users_permissions_user.profilePicture.formats.thumbnail.url
const userName = line.users_permissions_user.username
const title = line.title
const lineBody = line.line
const rating = line.rating
const categories = line.categories.map((category, index) => category.categoryName)
return {
profileImage,
userName,
title,
lineBody,
rating,
categories
}
})
useEffect(() => {
// calling the API with get method to fetch the data and store it inside APIdata state
axios.get(URL).then((res) => {
setAPIdata(res.data)
})
setLines(linesFromApi)
}, [URL, linesFromApi])
return (
<div>
// mapping through the lines list and rendering a card for each element
{lines.map((line, index) => <LineCard line={line} />)}
</div >
);
}
export default App;
i know for sure that this is causing the problem
return (
<div>
{lines.map((line, index) => <LineCard line={line} />)}
</div >
);
my problem is that react keeps sending GET requests constantly, and i want it to stop after the first time it has the list.
how can i do that!
Try adding a check in your hook so that it restricts the api call if the value is already set.
Something like this
useEffect(() => {
if(lines.length === 0){
axios.get(URL).then((res) => {
setAPIdata(res.data)
})
setLines(linesFromApi)
}
}, [URL, linesFromApi])
You need to add the key property to the element in a map.
<div>
{lines.map((line, index) => <LineCard key={index} line={line} />)}
</div>

API based search using React

I am new to React and I want to run API based search. I have written a sample code with the search functionality but it is not working as per requirement. I want to search from the list but it is always giving the same array of as the whole list when I am writing anything in the search box.
Please help me out and let me know where the code in wrong.
Here is my code:
TestEntry.js
import React , {useState,useEffect} from 'react'
import {Table} from 'reactstrap'
import {Navbar,Nav,NavDropdown,Form,FormControl,Button} from 'react-bootstrap'
//import axios from 'axios'
import Loading from './loading.gif';
const CoinGecko = require('coingecko-api');
const CoinGeckoClient = new CoinGecko();
function TestEntry(){
const[item,SearchData]=useState([]);
const[cryptos,setCryptos]=useState([]);
useEffect(()=>{
fetchItems()
},[])
const fetchItems=async()=>{
const url="https://api.coingecko.com/api/v3/coins/list";
const response= await fetch(url);
const info=await response.json();
console.log(info);
setCryptos(info);
}
const Search=(key)=>{
console.log(key);
fetch("https://api.coingecko.com/api/v3/coins/list?q="+key)
.then((data)=>{
data.json().then((resp)=>{
console.warn("resp:",resp)
SearchData(resp)
})
})
}
const cryptoJsx=cryptos.map(crypto=>(
<div key={crypto.id}>
{crypto.id}
</div>
));
return(
<div>
Search:
<input type="text" onChange={(event)=>Search(event.target.value)}/>
<div>
{
{item} ?
<div>
{
item.map((items)=>
<div key={items.id}>{items.name}</div>
)
}
</div>
: ""
}
</div>
{cryptoJsx}
</div>
)
}
export default TestEntry
The search api seems not working. When I tried api-search for a text separately in browser, it returned full results.
Anyway....
You can do search locally i.e. filter the cryptos array. It will cause re-render and only filtered results are shown.
Note:
Maintain a copy of the cryptos and always filter based on original and only mutate the cryptos. This way search works (both typing a char and deleting a char) and search results are re-rendered automatically
The downside of filtering state data is that new data from server is only obtained in client upon page refresh
If you really want to use api docs and use correct endpoint. Also consider using debouncing. Perform debounce in React.js
I have checked this and search is working fine.
import React, { useState, useEffect } from "react";
function TestEntry() {
const [item, SearchData] = useState([]);
const [cryptos, setCryptos] = useState([]);
const [origCryptosCount, setOrigCryptosCount] = useState([]);
useEffect(() => {
fetchItems();
}, []);
const fetchItems = async () => {
const url = "https://api.coingecko.com/api/v3/coins/list";
const response = await fetch(url);
const info = await response.json();
setCryptos(info);
setOrigCryptosCount(info);
};
// const Search_Old = key => {
// console.log(key);
// fetch("https://api.coingecko.com/api/v3/coins/list?q=" + key).then(data => {
// data.json().then(resp => {
// SearchData(resp);
// });
// });
// };
//
const Search = key => {
const newResults = origCryptosCount.filter(crypto => crypto.name.includes(key));
console.log('newResults', newResults);
setCryptos(newResults);
};
const cryptoJsx = cryptos.map(crypto => (
<div key={crypto.id}>{crypto.id}</div>
));
return (
<div>
Search:
<input type="text" onChange={event => Search(event.target.value)} />
{cryptoJsx}
</div>
);
}
export default TestEntry;

Categories

Resources