Display firebase data with react - reactjs

I'm new to react, so please give me some slack. I'm trying to extract data from a collection in firebase based on the url and display it on the page. My attempt is below:
import React, { useEffect, useState, map } from "react";
import { useParams } from "react-router-dom";
import { db } from "../../firebase";
import { doc, getDoc } from "firebase/firestore";
export default function UserPage() {
const { userUrl } = useParams();
const [user, setUser] = useState();
const docUserInfo = doc(db, "userinfo", userUrl);
useEffect(() => {
getDoc(docUserInfo).then((doc) => {
setUser(doc.data());
});
});
return (
<>
<div>
{user.map((user) => (
<div>{user.title}</div>
))}
</div>
</>
);
}
I get an error saying:
TypeError: Cannot read properties of undefined (reading 'map')
According to other posts here, this error suggests that my object (user) is not an array, and it doesn't seem to be. I tried using typeof to understand what type of object it is, but it just says "object". So I checked using Array.isArray(), which returns false, but here is where I get stuck.
Using setUser(doc.data().title) gives me what I want, sort of, but I want all the items inside the user array and don't want to create a useState for all of them. I also don't think there is an issue with useParams().
In addition, sometimes undefined is returned and sometimes this is done in an infinite loop, but I haven't figured out exactly when this happens.
Lastly, there are a lot of tutorials for exactly what I'm trying to do, but all of them are just displaying the fetched data in in the console (which I'm able to do), or they are using firebase<v9.

Your code loads a single document from Firestore, so there's no need to loop over multiple users as you'd do with the user.map that you have. Instead, just display the properties from your user state variable:
return (
<>
<div>
<div>{user.title}</div>
</div>
</>
);

Related

How to use the useQueries hook in react-query v4

So I'm perfectly able to retrieve matches from a competition with the Football-data.org API and display them in my react/typescript application using the useQuery hook from react-query:
import {CompetitionProps} from "../App";
import {getMatchesFromApi, Match} from "../api/GetMatchesFromApi";
import {List, ListItem} from "#mui/material";
import {useQuery} from "#tanstack/react-query";
function MatchesList({competitions}: CompetitionProps) {
const { isLoading, error, data, isFetching} = useQuery(["matches"], async () => {
return await getMatchesFromApi(competitions);
});
if (isLoading || isFetching) {
return (<div>Loading</div>);
} else {
return (
<List>
{data?.map((match: Match) => {
return (
<ListItem key={match.id}>{match.homeTeam.shortName} - {match.awayTeam.shortName}</ListItem>
);
})}
</List>
);
}
}
export default MatchesList;
However I want all matches from a list of competitions (the competitions can be different based on user preferences). When reading the docs of react-query, the useQueries hook should do the trick. These docs unfortunately don't show how to handle the results of the useQueries hook:
https://tanstack.com/query/v4/docs/reference/useQueries
I tried to use it like this:
import {
useQueries,
UseQueryOptions,
UseQueryResult
} from "#tanstack/react-query";
import { getMatchesFromApi, Match } from "./GetMatchesFromApi";
const allCompetitions = [2003, 2021];
function MatchesList() {
const results = useQueries({
queries: allCompetitions.map<UseQueryOptions<Match[]>>(
(competition: number) => {
return {
queryKey: ["competition", competition],
queryFn: async () => await getMatchesFromApi(competition)
};
}
)
});
return <div>{results.length}</div>;
}
export default MatchesList;
Even though I'm not even attempting to display the data yet, just using this code that only prints the length of the results array, will cause the code to fetch every few seconds. This will result in 429 (too many requests) responses of the football-data.org API fairly quickly.
This behavior doesn't match at all with the default staleTime and cacheTime settings explained in:
https://medium.com/doctolib/react-query-cachetime-vs-staletime-ec74defc483e
The question: How do I stop this infinite fetching loop? The reason I wanted to use react-query at all is to lazy fetch the match data only once.
Full project to reproduce this problem:
https://codesandbox.io/s/serene-raman-47n2uz
(If you want to reproduce it, you'll have to register on football-data.org for free and generate an API key. Put your key in the Key.ts file. I don't want to put mine on the internet).
Thanks!

useEffect not being executed in React

This seems to be a common problem but somehow I simply cannot get it to work : I am trying to read some data from a mongoDB database. If I call the NodeJS server directly in a browser, as in
http://localhost:5000/record/nilan
I get the data as a JSON string:
{
"pid":{
"ck":"F19909120:525.522.8788.37",
"name":"nilan",
"cID":"0SL8CT4NP9VO"
}
}
But when I am calling this from a React function RecordDetails(), I get nothing. Please see the code below :
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { useParams, useNavigate } from "react-router";
export default function RecordDetails() {
const params = useParams();
const [record1, setRecords] = useState([]);
window.alert('here1');
// This method fetches the records from the database.
useEffect(() => {
async function get1Record() {
//const id = params.id.toString();
const response = await fetch(`http://localhost:5000/record/${params.id.toString()}`);
if (!response.ok) {
const message = `An error occurred: ${response.statusText}`;
window.alert(message);
return;
}
const record1 = await response.json();
const message2 = 'here'
window.alert(message2);
window.alert(record1.pid.name);
window.alert(record1.pid.ck);
setRecords(record1);
}
get1Record();
return;
} , [record1.length]);
window.alert('here2');
// This following section will display the table with the records of individuals.
return (
<div>
<h3>Record Details</h3>
{record1.pid.name}
{record1.pid.ck}
{record1.pid.cID}
</div>
);
}
The process does not seem to be entering the useEffect() part of the code .
When this function RecordDetails() is called, we get the alerts in this sequence. "here1", "here2", "here1", "here2"; then the functions terminates with a blank screen.
I do not get any of the alerts from inside the useEffect, not even the error message alert. What am I doing wrong?
The useEffect hook only runs after the DOM has rendered and when the values inside the dependency array have changed; since you don't change the values inside the dependency array anywhere except within the useEffect hook, it never runs because record1.length will never change -- the code is never reached. You may want to use the useMemo hook instead, which runs once and then will only change when its dependency changes. Also, in order to get it to update, you need to trigger the change from outside the hook, not within, otherwise it will not run. Thus, may I suggest something along the following:
const userInfo = useMemo(() => {
const response = //make server call
return response
}, [dependentValues]);
setRecords({record1: userInfo});
return (
<div onChange={() => {changeDependentValues()}} />
)

Problems with fetch calls with Axios in React

I am trying to use the https://api.randomuser.me/ API to get the data from a person. As for now, I am trying to get only his first and second names, and date of birth, but I don't know if I am doing the call to the API wrong or what I am doing incorrectly. This is the code I have:
import Axios from 'axios'
import { useState } from 'react';
function FetchCall() {
const[name,setName] = useState("")
const[birth,setBirth] = useState("")
const getInfo = () => {
Axios.get("https://api.randomuser.me/").then(
(response) => {
setName(response.results.name.first + " " + response.results.name.last);
setBirth(response.dob.date);
}
);
};
return(
<div>
Hello
<button onClick={getInfo}>Get User</button>
{name}
{birth}
</div>
);
}
export default FetchCall;
I am getting a "Unhandled Rejection (TypeError): Cannot read property 'name' of undefined" error when clicking on the button.
Axios response stores response json in .data field
response.results should be changed into response.data.results
Also, just a note, results is an array, so you need to apply indexer as well. For example, response.data.results[0].name.first
Check out React dev tools to set breakpoints and find which item in sequence is undefined

React.js Router url is resolving from browser but not from Link within App

I have a component setup in react router that has the following url
http://localhost:3000/editing/5f0d7484706b6715447829a2
The component is called from a parent component using the <Link.. syntax and when I click the button in the parent component the url above appears in the browser but my app crashes.
The error applies to the first manipulation the component does with the data - which indicates to me that the app is running before the data is available. This does not happen elsewhere in the app.
×
TypeError: Cannot read property 'reduce' of undefined
If I then refresh the url in the browser it works as intended. The component also loads fine if I just paste the above url into the browser.
The code is below
const Button = (props) => {
//main props
const { buttonFunction, buttonIconName, buttonStyle, visible } = props;
return (
<Link
to='./editing/'+props.documentId}
</Link>
);
};
export default Button;
The called component is below
mport React, { useContext, useEffect } from 'react';
import DocumentEditor from '../documentEditor/documentEditor';
import DocumentContext from '../../context/Document/DocumentContext';
import Spinner from '../layout/Spinner';
//For testing this Id works
// 5f0d7484706b6715447829a2
//Wrappers
const Editing = ({ match }) => {
//styling
const documentContext = useContext(DocumentContext);
const { Documents, myFiltered, getDocuments, loading, getDocument } = documentContext;
useEffect(() => {
getDocument(match.params.documentid);
// eslint-disable-next-line
}, []);
return (
<div>
{Documents !== null && !loading ? (
<DocumentEditor
Document={Documents}
DocumentContext={documentContext}
documentMode='edit'
/>
) : (
<Spinner />
)}
</div>
);
};
export default Editing;
My Thoughts
I suspect it may be due to the form loading prior to the data has been fetched but I don't see how as the code
{Documents !== null && !loading ? (
along with my functions that have been pulling data have been working fine for ages and use async / await should prevent this. Maybe I shouldn't be using a link in this way?
As #Yousaf says in the comments, your specific error is regarding a reduce() call, and we can't see that in the code you posted.
I do see what appears to be a typo, and that may be your issue.
At the top of your file, you're importing DocumentContext with a capital "D"
import DocumentContext from '../../context/Document/DocumentContext';
And inside your Editing component, you're destructuring documentContext with a lowercase "d":
const { Documents, myFiltered, getDocuments, loading, getDocument } = documentContext;
This would lead to { Documents, myFiltered, getDocuments, loading, getDocument } all being undefined. You're then passing Documents and documentContext to the DocumentEditor, which presumably is trying to call reduce() on one of those things that are passed, which are undefined.

React context not saving updates

I created a react project for music/artists.
I'm using the React context for accessing artists data across components:
import React, { createContext, useState } from "react";
export const ArtistContext = createContext(null);
const ArtistContextProvider = props => {
const [artists, setArtists] = useState([]);
const addArtist = new_artist => {
setArtists([...artists, new_artist])
};
return (
<ArtistContext.Provider value={{artists, addArtist}}>
{props.children}
</ArtistContext.Provider>
);
};
export default ArtistContextProvider;
I can successfully get the data from the artist context object and also execute de addArtists methods, but the artist I pass to the function is not saved and the variable stills returns the default value.
My usage in a component:
const { artists, addArtist } = useContext(ArtistContext);
useIonViewWillEnter(() => {
api.get('artists?manager=1'))
.then(function(response) {
artist = response.data.items[0];
addArtist(artist);
window.location.href = '/artist/' + artist.id + '/tabs';
})
.catch(error => console.log(error));
});
If i log inside the addArtist method, i can see that the artist object is valid, but then if i log again the artists array, it has the initial value.
I have tried different methods and syntax like class componenets with state, functional components with useState... So i'm guessing my syntax is correct but i'm missing some key concept about React.
Its my first project with React, so i'm probably missing something, but i can not figure it out even after reading the context documentation.
You need to store your state/context object to localStorage, sessionStorage, cookies or server since accessing a different url path refreshes the page.
This link may help you store your state when you refresh the page.
How to maintain state after a page refresh in React.js?
Just encountered this today. Took me 2 days to resolve this lol

Resources