I have a list of user on a page , what i want to achieve is render user detail when i click on the link with his id. for that i have tried to send the id and retrieve the data with a new request like this :
My routes :
<Route path='/coupeurs' element={<Coupeurs />} />
<Route path='/coupeurs/add' element={<AddCoupeur />} />
<Route path='/coupeurs/:id' element={<Coupeur />} />
My list :
<ul>
{coupeurs.map(coupeur => (
<li key={coupeur.id}>
{coupeur.nom} {coupeur.prenom}
<div className='coupeur-actions'>
<Link to={`/coupeurs/${coupeur.id}`}>Voir</Link>
<Link to={`/coupeurs/${coupeur.id}/edit`}>Editer</Link>
</div>
</li>
))}
</ul>
the place where i want display details :
export default function Coupeur(props) {
console.log(props);
let { id } = useParams();
const [coupeur, setCoupeur] = useState(getCoupeurInfoById(id));
console.log(coupeur);
return (
<div>
<h3>Identifiant : {id}</h3>
<h3>Nom : {coupeur.nom}</h3>
<h3>Prenom : {coupeur.prenom}</h3>
<h3>Statut : {coupeur.statut}</h3>
</div>
)
}
My getCoupeurInfoById function :
export async function getCoupeurInfoById(id) {
const docRef = doc(db, "coupeurs", id);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
return docSnap.data();
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}
i get my page without the info , but when i console log my variable i can see the result !
Can you help me found the right way to do that kind of things ? thank you
Since getCoupeurInfoById is an async function it implicitly returns a Promise object and subsequently (quickly) resolves and mutates the coupeur state variable. When you log it you see the resolved value.
The useState hook expects a synchronous function that returns the initial state value.
Use a useEffect hook to handle the side-effect of fetching/computing the coupeur value.
Example:
export default function Coupeur(props) {
const { id } = useParams();
const [coupeur, setCoupeur] = useState(); // <-- initially undefined
useEffect(() => {
const getCoupeur = async () => {
try {
const coupeur = await getCoupeurInfoById(id);
setCoupeur(coupeur);
} catch(error) {
// handle any rejections/errors/etc
}
};
getCoupeur(); // <-- fetch/compute coupeur value
}, [id]);
if (!coupeur) return null; // or loading indicator/etc
return (
<div>
<h3>Identifiant : {id}</h3>
<h3>Nom : {coupeur.nom}</h3>
<h3>Prenom : {coupeur.prenom}</h3>
<h3>Statut : {coupeur.statut}</h3>
</div>
)
}
Related
I'm new to react and trying to connect firestore for my project.
I followed the example from the Internet and everything works, the data is deleted and written to the database, also when I change the data they change in the database, but I get errors in the console and a white screen.
Uncaught TypeError: data.map is not a function
If you need any more files or code, I will correct my question, please write which ones I need to add
Also, when loading the page, I get the following error in the console:
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist. at wrappedSendMessageCallback
Here is the code that throws the error:
export default function Saved({ data, setData }) {
function editData(id, newWord, newTranslate, newNote) {
const editedDataList = async (card) => {
if (id === card.id) {
return {
...card,
word: newWord,
translate: newTranslate,
note: newNote,
};
}
let newFields = {
word: newWord,
translate: newTranslate,
note: newNote,
}
await updateDoc(doc(db, "db-name", id), newFields);
console.log(newFields)
return card;
};
setData(editedDataList);
}
const deletePost = async (id) => {
await deleteDoc(doc(db, "db-name", id));
};
const dataList = data.map((card) => (
<SavedData
id={card.id}
key={card.id}
word={card.word}
translate={card.translate}
note={card.note}
editData={editData}
del={deletePost}
/>
));
return (
<div>
<div className="sec-menu"></div>
<div className="saved-inner">
{data.length >= 1 ? (
<div className="saved-list">{dataList}</div>
) : (
<Link className="main-btn" to="/addcard">
Add
</Link>
)}
</div>
</div>
);
}
Here Menu.js code:
function Menu() {
const [data, setData] = useState([]);
useEffect(() => {
const q = query(collection(db, "db-name"));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
let wordsArr = [];
querySnapshot.forEach((doc) => {
wordsArr.push({ ...doc.data(), id: doc.id });
});
setData(wordsArr);
});
return () => unsubscribe();
}, []);
return (
<div className="content">
<AuthContextProvider>
<Routes>
<Route
path="saved"
element={<Saved data={data} setData={setData} />}
/>
</Route>
</Routes>
</AuthContextProvider>
</div>
);
}
export default Menu;
On second glance, the issue is where you call setData(editedDataList). You're passing in a function into this method which is in turn updating data to be a function instead of an array. Try changing, editData() to be something like this:
const editData = async (id, newWord, newTranslate, newNote) => {
const editedDataList = await Promise.all(data.map(async (card) => {
let newFields = {
word: newWord,
translate: newTranslate,
note: newNote,
};
if (id === card.id) {
return { ...card, ...newFields };
}
await updateDoc(doc(db, "db-name", id), newFields);
console.log(newFields);
return card;
}));
setData(editedDataList);
};
editedDataList will be an array of the modified cards in the original and setData() should work as expected.
maybe the error occurs because "data" object is not an array.
And check what are you setting on "setData(editedDataList);" instruction
I was wonder what I'm doing wrong here.
I'm getting this error: "Rendered more hooks than during the previous render."
export default function ProductDetails() {
//Use State
const {qty, increaseQty, decreaseQty, onAdd, setQty} = useStateContext();
//Reset Qty
useEffect(() => {
setQty(1);
}, []);
//Fetch Slug
const {query} = useRouter();
//Fetch Graphql data
const [results] = useQuery({
query: GET_PRODUCT_QUERY,
variables: {slug: query.slug}
})
const {data, fetching, error} = results;
//Check for data coming in
if(fetching) return <p>Loading...</p>;
if(error) return <p>Oh no....</p>;
//Extract Product Data
const {title,description, image, gallery } = data.products.data[0].attributes;
const [img, setImg] = useState(gallery.data[0].attributes.formats.medium.url);
console.log(img);
//Create a toast
const notify = () => {
toast.success(`${title} added to your cart`, {duration: 1500});
}
return(
<DetailsStyle>
<Gallery>
<img src={gallery.data[0].attributes.formats.medium.url} alt={title} />
<Thumbnails>
{gallery.data.map((image, index) => (
<SingleThumb key={index} >
<img src={image.attributes.formats.thumbnail.url} alt={title} />
</SingleThumb>
)
)}
</Thumbnails>
</Gallery>
<ProductInfo>
<h3>{title}</h3>
<p>{description}</p>
<Quantity>
<span>Quantity</span>
<button><AiFillMinusCircle onClick={decreaseQty} /></button>
<p>{qty}</p>
<button><AiFillPlusCircle onClick={increaseQty}/></button>
</Quantity>
<Buy onClick={() => {
onAdd(data.products.data[0].attributes, qty)
notify();
}}>Add To Cart</Buy>
</ProductInfo>
</DetailsStyle>
)
}
Something wrong is in this line: const [img, setImg] = useState();
Why I can't use more hooks here.
Does anyone know why I'm getting this?
You are using early return
and this line of code won't execute every time:
const [img, setImg] = useState(gallery.data[0].attributes.formats.medium.url);
This is only conditionally called:
const [img, setImg] = useState(gallery.data[0].attributes.formats.medium.url);
Because the component has earlier conditional return statements. Move it to earlier in the function. (Generally I invoke useState operations right away.)
Hooks need to always be consistently called in the same order on every render.
You declare your state after some return statements. It means that if you had any errors or you were in loading state, the state is not defined. But maybe in the next render, the data is set and then your state will be defined with the inital value (gallery.data[0].attributes.formats.medium.url).
It's forbidden in react because all of the hooks should always be in the same order on every single render. In order to fix this, you should change the place of your useState for img.
Hope it helps:
export default function ProductDetails() {
const [img, setImg] = useState('');
//Use State
const {qty, increaseQty, decreaseQty, onAdd, setQty} = useStateContext();
//Reset Qty
useEffect(() => {
setQty(1);
}, []);
//Fetch Slug
const {query} = useRouter();
//Fetch Graphql data
const [results] = useQuery({
query: GET_PRODUCT_QUERY,
variables: {slug: query.slug}
})
const {data, fetching, error} = results;
//Check for data coming in
//Extract Product Data
useEffect(() => {
if(results && results.data) {
const {data} = results
const { gallery } = data.products.data[0].attributes;
setImg(gallery.data[0].attributes.formats.medium.url);
}
}, [results]);
useEffect(() => {
console.log(img);
}, [img]);
//Create a toast
const notify = (title) => {
toast.success(`${title} added to your cart`, {duration: 1500});
}
if(fetching) {
return <p>Loading...</p>;
} else if(error) {
return <p>Oh no....</p>;
} else if(data) {
const { title, description, image, gallery } = data.products.data[0].attributes;
return(
<DetailsStyle>
<Gallery>
<img src={gallery.data[0].attributes.formats.medium.url} alt={title} />
<Thumbnails>
{gallery.data.map((image, index) => (
<SingleThumb key={index} >
<img src={image.attributes.formats.thumbnail.url} alt={title} />
</SingleThumb>
)
)}
</Thumbnails>
</Gallery>
<ProductInfo>
<h3>{title}</h3>
<p>{description}</p>
<Quantity>
<span>Quantity</span>
<button><AiFillMinusCircle onClick={decreaseQty} /></button>
<p>{qty}</p>
<button><AiFillPlusCircle onClick={increaseQty}/></button>
</Quantity>
<Buy onClick={() => {
onAdd(data.products.data[0].attributes, qty)
notify(title);
}}>Add To Cart</Buy>
</ProductInfo>
</DetailsStyle>
)
} else {
return null;
}
}
There should be no return before hooks.
These lines
if(fetching) return <p>Loading...</p>;
if(error) return <p>Oh no....</p>;
should be after all hooks
enter image description here
When I typed this http://localhost:3000/hell/:2 the page loads but will not fetch a single item that has id 2
But When I clicked the button then the page shows the single item that has id 2
I need to show the data when I entered http://localhost:3000/hell/:2 as URL
""""""""""CODE Gets RUN but it was showing in a paragraph format so I had edit and made code easier to understand """""""""
the code is --->>
At App.js-->
<div className="App">
<div className='body'>
<Router history={History}>
<Switch>
<Route path="/hell/:id"><Hell/></Route>
</Switch>
</Router>
</div>
</div>
At Hello.js-->
let {id} = useParams();
let di = id;
const [loading,setloading] = useState([false]);
const [posts,setposts] = useState([]);
const [search,setsearch] = useState("");
const [message,setmessage] = useState("");
const history = useHistory();
useEffect(()=>{
const getload = async ()=>{
setloading(true);
const response = await axios.get(`http://127.0.0.1:8000/list/`);
const message = "Error";
setposts(response.data);
setmessage(message);
setloading(false);
}
},[]);
console.log({di});
function inputhandler(a){
id = a;
history.push(generatePath("/hell/:id", { id }));
setsearch(a);
}
return (
<div>
<h1>Find : {id}</h1>
{
posts.map((curElem) =>{
return(
<div>
<Link key={curElem.id} to={curElem.id} onClick={() => inputhandler(curElem.id)}>{curElem.title}</Link>
</div>
)
})
}
{
loading ?(<h4>Loading...{message}</h4>):(
(posts.filter((value)=>{
if(value.id===(search)){
return message;
}
})
.map(item =><Help key={item.id} title={id}></Help>)))
}
</div>
)
}
You've accessed the id route match param but then never used it.
You do not form the link targets correctly. Once you are linking correctly then there is no need for the extraneous onClick handler to set any search state with the item.id value since you can consume the linked id right from the params.
Remember to also call getload so the posts state is updated.
Hello.js
const history = useHistory();
const { id } = useParams(); // <-- current `id` from "/hell/:id"
const [loading,setloading] = useState([false]);
const [posts,setposts] = useState([]);
const [message,setmessage] = useState("");
useEffect(()=>{
const getload = async ()=>{
setloading(true);
const response = await axios.get(`http://127.0.0.1:8000/list/`);
const message = "Error";
setposts(response.data);
setmessage(message);
setloading(false);
}
getload(); // <-- need to call to make the GET request
},[]);
return (
<div>
<h1>Find : {id}</h1>
{posts.map((curElem) => {
return(
<div key={curElem.id}> // <-- React key on outermost element
<Link
// generate target path here
to={generatePath("/hell/:id", { id: curElem.id })}
>
{curElem.title}
</Link>
</div>
)
})}
{loading
? <h4>Loading...{message}</h4>
: posts.filter((value) => value.id === id) // <-- filter by id
.map(item => <Help key={item.id} title={id}></Help>)
}
</div>
)
I made a search bar that allows the user to search all sports available in one specific city (if sport is not defined) or a specific sport in a specific city (if sport is defined).
City will allways be defined.
I put 2 inputs (city and sport) on my searchbar and I want immediate results (so that there is a request to my API without any button "search" that triggers the request).
So when the user types something on the city input it triggers a request to the API and when he types something on the sport input it retriggers the request but this time with the city and the sport defined.
Both inputs values are store in states (city and sport).
I manage to do something that seems to work, the only problem is that if I types a sport in my input search, it does not update my request to the API. I have to retype the sport in my input a second time so that the request is updated.
I don't know why it does not update the first time I types something in my sport input because I have specified on my useEffect array that it must re render when the sport state changes.
Can someone help me understand this ?
My code :
import React, { useState, useEffect } from "react";
import style from "../styles/pdrs.module.css";
import axios from "axios";
import SearchBar from "../components/SearchBar";
const Pdrs = ({ setSearchCity, searchSport, setSearchSport }) => {
// if request's result is loading
const [isLoading, setIsLoading] = useState(false);
// search result
const [searchresults, setSearchresults] = useState(
"Lancez une recherche avec au moins une ville !"
);
// state for the searchbar request
const [city, setCity] = useState("");
const [sport, setSport] = useState(0);
// get city's id for API's request
const fetchCity = async () => {
setIsLoading(true);
try {
// city search
const cityResponse = await axios.get(
`${baseAPI}/city/name=${searchCity}`
);
const city = cityResponse.data;
setCity(city);
setIsLoading(false);
} catch (error) {
console.log(error.message);
setIsLoading(false);
}
};
//fetching sport id
const fetchSport = async () => {
setIsLoading(true);
try {
const sportResponse = await axios.get(
`${baseAPI}/activity/name=${searchSport}`
);
setSport(sportResponse.data.data[0].macro_activity_id);
setIsLoading(false);
} catch (error) {
console.log(error.message);
}
};
//fetching final request response
const fetchDataRequest = async () => {
try {
setIsLoading(true);
const results = await axios.get(
`${baseAPI}/pdrs?city_id=${city.id}${
sport ? "¯o_activity_id=" + sport : ""
}`
);
// manage search results
if (results.data.nb_results === 1) {
setSearchresults({
data: [results.data.data],
nb_results: 1,
});
setNbResults(1);
setIsLoading(false);
} else {
setSearchresults(results.data);
setNbResults(results.data.nb_results);
setIsLoading(false);
}
} catch (error) {
console.log(error.message);
setSearchresults(
"Sorry, nothing was found... !"
);
}
};
useEffect(() => {
if (searchCity) {
fetchCity();
}
if (searchSport) {
fetchSport();
}
}, [searchCity, searchSport]);
useEffect(() => {
if (searchCity) {
fetchDataRequest();
}
}, [searchCity, searchSport]);
console.log(searchresults);
return <>
<main className={`container ${style.pdrs}`}>
<section className={style.searchbar}>
<SearchBar
searchCity={searchCity}
setSearchCity={setSearchCity}
searchSport={searchSport}
setSearchSport={setSearchSport}
searchInstallation={searchInstallation}
setSearchInstallation={setSearchInstallation}
searchType={searchType}
setSearchType={setSearchType}
setPage={setPage}
/>
</section>
<section className={style.results}>
{isLoading ? (
<div>Loading...</div>
) : typeof searchresults === "string" ? (
<div className={`${style.container} ${style.noResults}`}>
<h2>{searchresults}</h2>
</div>
) : (
<>
<div className={style.container}>
<div className={style.resultsList}>
{searchresults.data.map((pdrs) => {
return (
// some code displaying the result
);
})}
</div>
</div>
</>
)}
</section>
</main>
</>;
};
export default Pdrs;
Since you are having two useEffect and one is setting city and sport you would need to make debounce for making a call for fetching list by itself.
I would suggest that you firstly make changes to your use effect for API call fetchDataRequest:
useEffect(() => {
if (searchCity) {
fetchDataRequest();
}
}, [city, sport]);
You would listen to the actual data from BE, not from input that you fill.
And secondly you can use library useDebounce from here https://www.npmjs.com/package/use-debounce and use useDebounceCallback to delay calling API call after you select sport/city.
I am trying to have this component load data depending on its current url whether /global or /my-posts. The useEffect() grabs the data from the first loading of the component but when i change to another url i expected useEffect to check the url again and load the correct data but instead i'm stuck with the data from the first load. How do i get useEffect to invoke every time i click between different urls like /global and /my-posts url.
export default function Dashboard() {
const [allRecipes, setAllRecipes] = useState([]);
const [myRecipes, setMyRecipes] = useState([]);
const currentUrl = window.location.pathname;
useEffect(() => {
if (currentUrl === '/dashboard/global') {
console.log('hello');
trackPromise(
RecipeService.getAllRecipes()
.then((data) => {
setAllRecipes(data);
}),
);
} else if (currentUrl === '/dashboard/my-posts') {
console.log('hi');
trackPromise(
RecipeService.getRecipes()
.then((data) => {
setMyRecipes(data);
}),
);
}
}, []);
console.log(window.location.pathname);
return (
<>
<div className="dashboard">
<DashboardHeader />
<div className="created-posts">
{allRecipes.length !== 0
? allRecipes.map((recipe) => <Post recipe={recipe} key={uuidv1()} />)
: null}
{myRecipes.length !== 0
? myRecipes.recipes.map((recipe) => <Post recipe={recipe} key={uuidv1()} />)
: null}
{currentUrl === '/dashboard/create' ? <CreateForm /> : null}
<LoadingIndicator />
</div>
</div>
</>
);
}
to make React.useEffect run on every currentUrl change, you have to add it to useEffect dependencies array.
// first we need to control the state of window.location.pathname by react not the browser
// and make react state be the only source of truth.
const pathname = window.location.pathname
// manage currentUrl in state.
const [currentUrl, setCurrentUrl] = React.useState(pathname)
React.useEffect(() => {
setCurrentUrl(pathname)
}, [pathname])
// now you would add the contolled `currentUrl` state to its useEffect deps.
useEffect(() => {
if (currentUrl === '/dashboard/global') {
// ..........
} else if (currentUrl === '/dashboard/my-posts') {
// ..........
}
}, [currentUrl]);