Why is my fetched data not appearing in my React hooks component? - reactjs

I am trying to load data into my component for it to be displayed. I thought the issue was that I wasn't using async/await for the fetch, but even after adding that it still is not loading. I am logging out the "offerings" and it is just showing the empty array. How do I keep the component from rendering until after the data is loaded??
Thanks in advance!
const [offerings, setOfferings] = useState([]);
const loadData = async () => {
const res = await fetch(`http://52.207.83.69` + `${CRUD_OFFERING}`);
setOfferings(await res.json());
console.log(offerings, 'offerings')
};
useEffect(async () => {
navbarToggle();
await loadData();
}, []);
const dispatch = useDispatch();
const modalState = useSelector((state) => state.modal);
const modalToggle = () => {
dispatch({
type: MODAL_TOGGLE,
payload: !modalState.show,
});
ga.event("navbar_requestdemo_clicked");
};
const navbarOpenState = useSelector((state) => state.navbar);
const navbarToggle = () => {
if (!navbarOpenState.open) return;
dispatch({
type: NAVBAR_OPEN,
payload: false,
});
};
return (
<div
className="d-flex justify-content-center align-items-center bg-color-white fc-px-15"
onClick={navbarToggle}
>
<div className={homeStyles["padded-body"] + " col-11 p-0"}>
<div className=" position-relative bg-color-white">
<div className={homeStyles["img-holder"]}></div>
<div className="col-12 column position-absolute top-0 d-flex justify-content-center">
<div className="col-lg-6 col-12 fc-mt-2">
<SearchBar />
</div>
</div>
<div className="position-absolute top-50 translateY-middle">
<div className="position-relative">
<h1 className={`${homeStyles["hero-text"]} font-weight-bolder`}>
Building
<br />
Meaningful
<br />
Engagement
</h1>
<button
className="btn btn-primary-round mt-3 px-3 py-2"
onClick={() => {
modalToggle();
}}
>
Request access
</button>
</div>
</div>
</div>
<div
id={homeStyles["discover-section"]}
className="d-flex justify-content-center align-items-center"
>
<div className="col-12 column">
<h4 className="font-weight-bold">Discover</h4>
<div
id={homeStyles["offer-section"]}
className="row justify-content-center align-items-center"
>
{!offerings?.length &&
<h4 className="text-center">There are no active offerings.</h4>
}
</div>
<OfferingCarousal
offeringsList={offerings}
name={"Offerings"}
/>
<div id={homeStyles["consultancy-section"]} className="">
<div className="row">
<div
className="d-flex justify-content-center align-items-center col-lg-6 col-12 px-0 mt-3 mb-4"
id={homeStyles["consultancy-div"]}
>
<div className="col-12 column p-5">
<h1 className="font-weight-bold">Add your consultancy</h1>
<h5 className="mt-4">
Reach more people and organizations
</h5>
<Link href="/consultancies">
<button className="btn btn-primary-round mt-4">
Learn more
</button>
</Link>
</div>
</div>
<div className="col-lg-6 col-12 px-0">
<img
src="/images/Rachael_glasses_home_page.jpg"
id={homeStyles["consultant-img"]}
className="mt-3"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default HomeNew;

You can check if the data is present before you consume the data.
const [offerings, setOfferings] = useState([]);
const loadData = async () => {
const res = await fetch(`http://52.207.83.69` + `${CRUD_OFFERING}`);
setOfferings(res.json());
console.log(res, 'offerings')
};
useEffect(() => {
navbarToggle();
loadData(); // await has no affect inside useEffect for top level functions
}, []);
// removed internal code for clarity
const dispatch = useDispatch(...yourCode);
const modalState = useSelector(...yourCode);
const modalToggle = () => {...yourCode};
const navbarOpenState = useSelector(...yourCode);
const navbarToggle = () => {...yourCode};
// check after the hooks and before the consuming the data
if(!offerings && !offerings.length) return <>No offerings</>;
return <div>your side nav</div>;
};
It's also good practice to catch asynchronous errors as they occur to prevent your a single component form breaking your whole app. You can also take advantage of the try...catch and put in loading and error states too.
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [offerings, setOfferings] = useState([]);
const loadData = async () => {
try {
setError(false);
setLoading(true);
const res = await fetch(`http://52.207.83.69` + `${CRUD_OFFERING}`);
setOfferings(res.json());
} catch (e){
setError(true)
} finally {
setLoading(false);
}
};
// other code from above
if(error) return <>error</>;
if(loading) return <>loading</>;
if(!offerings && !offerings.length) return <>No offerings</>;
return <div>your side nav</div>;
};

async function can't be put in the useEffect hook directly.
https://prnt.sc/1lu7vdc
It can be like this.
useEffect(() => {
...
(async ()=>{
await loadData()
})();
}, []);
But in your case, I think you don't need to wait until loadData function is executed.
Just make sure you handle exceptions on the rendering for Empty data.

Related

Passing setState from useState hook as a prop in a firebase function or any other component

Currently not facing any problems I'm just wondering what the best practices for passing setState to a function or component, specifically a firebase function that is declared on another file or a form that is designed to be reusable.
This is a function is from my firebase file
`
export function getResumes({ applicationtState: applicationState }) {
onSnapshot(
query(collection(db, 'applications'), orderBy('timestamp', 'desc')),
(querySnapshot) => {
const arrays = []
querySnapshot.forEach((snap) => {
arrays.push(snap.data())
})
applicationState(arrays)
}
)
}
`
this is how i call the function on my resumes file
`
const [application, setApplication] = useState([])
useEffect(() => {
getResumes({ applicationtState: setApplication })
}, [])
`
In another instance I have a form component which whis i am calling on multiple pages
`
function EducationBox({
highSchoolState,
highSchoolValue,
highSchoolCourseOfStudyState,
highSchoolCourseOfStudyValue,
highSchoolGraduateState,
highSchoolNumberOfYearsCompletedState,
highSchoolNumberOfYearsCompletedValue,
highSchoolHonorsReceivedState,
highSchoolHonorsReceivedValue,
collegeState,
collegeValue,
collegeCourseOfStudyState,
collegeCourseOfStudyValue,
collegeGraduateState,
// collegeGraduateValue,
collegeNumberOfYearsCompletedState,
collegeNumberOfYearsCompletedValue,
collegeHonorsReceivedState,
collegeHonorsReceivedValue,
GradState,
GradValue,
GradCourseOfStudyState,
GradCourseOfStudyValue,
GradGraduateState,
// GradGraduateValue,
GradNumberOfYearsCompletedState,
GradNumberOfYearsCompletedValue,
GradHonorsReceivedState,
GradHonorsReceivedValue,
tradeState,
tradeValue,
tradeCourseOfStudyState,
tradeCourseOfStudyValue,
tradeGraduateState,
// tradeGraduateValue,
tradeNumberOfYearsCompletedState,
tradeNumberOfYearsCompletedValue,
tradeHonorsReceivedState,
tradeHonorsReceivedValue,
}) {
return (
<div className=" flex w-full grid-rows-4 flex-col rounded-[20px] outline outline-2 outline-[#b5b5b5]">
<SchoolItem
SchoolState={highSchoolState}
SchoolValue={highSchoolValue}
CourseOfStudyState={highSchoolCourseOfStudyState}
SchoolCourseOfStudyValue={highSchoolCourseOfStudyValue}
SchoolGraduateState={highSchoolGraduateState}
title={'High School'}
NumberOfYearsCompletedState={highSchoolNumberOfYearsCompletedState}
NumberOfYearsCompletedValue={highSchoolNumberOfYearsCompletedValue}
HonorsReceivedState={highSchoolHonorsReceivedState}
HonorsReceivedValue={highSchoolHonorsReceivedValue}
/>
<SchoolItem
SchoolState={collegeState}
SchoolValue={collegeValue}
CourseOfStudyState={collegeCourseOfStudyState}
SchoolCourseOfStudyValue={collegeCourseOfStudyValue}
SchoolGraduateState={collegeGraduateState}
title={'College'}
NumberOfYearsCompletedState={collegeNumberOfYearsCompletedState}
NumberOfYearsCompletedValue={collegeNumberOfYearsCompletedValue}
HonorsReceivedState={collegeHonorsReceivedState}
HonorsReceivedValue={collegeHonorsReceivedValue}
/>
<SchoolItem
SchoolState={GradState}
SchoolValue={GradValue}
CourseOfStudyState={GradCourseOfStudyState}
SchoolCourseOfStudyValue={GradCourseOfStudyValue}
SchoolGraduateState={GradGraduateState}
title={'Grad School'}
NumberOfYearsCompletedState={GradNumberOfYearsCompletedState}
NumberOfYearsCompletedValue={GradNumberOfYearsCompletedValue}
HonorsReceivedState={GradHonorsReceivedState}
HonorsReceivedValue={GradHonorsReceivedValue}
/>
<SchoolItem
SchoolState={tradeState}
SchoolValue={tradeValue}
CourseOfStudyState={tradeCourseOfStudyState}
SchoolCourseOfStudyValue={tradeCourseOfStudyValue}
SchoolGraduateState={tradeGraduateState}
title={'Trade School'}
NumberOfYearsCompletedState={tradeNumberOfYearsCompletedState}
NumberOfYearsCompletedValue={tradeNumberOfYearsCompletedValue}
HonorsReceivedState={tradeHonorsReceivedState}
HonorsReceivedValue={tradeHonorsReceivedValue}
/>
</div>
)
}
function SchoolItem({
SchoolState,
SchoolValue,
CourseOfStudyState,
SchoolCourseOfStudyValue,
SchoolGraduateState,
title,
NumberOfYearsCompletedState,
NumberOfYearsCompletedValue,
HonorsReceivedState,
HonorsReceivedValue,
}) {
return (
<div className=" flex w-full grid-cols-6 flex-col items-center justify-center px-10 text-center md:flex-row">
<h4 className=" flex text-lg font-bold"> {title}</h4>
<div className=" mx-3 w-full">
<TextInput
value={SchoolValue}
widthPercentage="w-full"
placeHolder="School Name"
onChange={(text) => {
SchoolState(text.target.value)
}}
/>
</div>
<div className=" mx-3 w-full">
<TextInput
widthPercentage="w-full"
placeHolder="Course Of Study"
onChange={(text) => {
CourseOfStudyState(text.target.value)
}}
value={SchoolCourseOfStudyValue}
/>
</div>
<div className=" mx-3 w-full">
<h5>Graduated?</h5>
<RadioButton answerState={SchoolGraduateState} />
</div>
<div className=" mx-3 w-full">
<TextInput
value={HonorsReceivedValue}
widthPercentage="w-full"
placeHolder="Honors Received"
onChange={(text) => {
HonorsReceivedState(text.target.value)
}}
/>
</div>
<div className=" mx-3 w-full">
<TextInput
value={NumberOfYearsCompletedValue}
widthPercentage="w-full"
placeHolder="# of years Completed"
onChange={(text) => {
NumberOfYearsCompletedState(text.target.value)
}}
/>
</div>
</div>
)
}
export default EducationBox
`
I just want to know what best practices is. Please Help!
you can take help of promises and use something like this. and this way your function could become more reusable could not be dependent on any args or state :-
export function getResumes() {
return new Promise((resolve, reject) => {
onSnapshot(
query(collection(db, 'applications'), orderBy('timestamp', 'desc')),
(querySnapshot) => {
const arrays = []
querySnapshot.forEach((snap) => {
arrays.push(snap.data())
})
resolve(arrays)
}
)
})
}
const [application, setApplication] = useState([])
useEffect(() => {
getData()
}, [])
const getData = async () => {
try {
const resumes = await getResumes()
setApplication(resumes)
} catch (err) {
console.log("error", err);
}
}

Rerender component on click

I'm trying to generate a new quote when the button is clicked. Having trouble figuring out how to implement this. Googling around has led me to believe that the useCallback hook is the way to go, but I haven't any experience with it so I have yet to have any luck implementing it. Any help is appreciated! Thank you in advance.
/* eslint-disable react-hooks/rules-of-hooks */
import React, { useEffect, useState, useCallback } from 'react'
const Main = () => {
const [quote, setQuote] = useState(null)
const [author, setAuthor] = useState(null)
const [newQuote, setNewQuote] = useState(false)
useEffect(() => {
fetch('https://type.fit/api/quotes')
.then(response => response.json())
.then((data) => {
let randomIndex = Math.floor((Math.random() * data.length));
setQuote(data[randomIndex].text)
setAuthor(data[randomIndex].author)
})
.catch(err => console.error(err));
}, [])
return (
<div id='main' className='grid place-items-center h-screen w-screen text-center'>
{/* Quote Gen Container */}
<div className='flex flex-col justify-start mx-auto bg-sky-300 w-3/4 h-3/4 text-black space-y-3 p-32 rounded-3xl relative'>
<h1 className='text-bold text-3xl absolute top-0 mx-auto'>Random Quote Generator</h1>
<div>
<h4 id="text">{`"${quote}"`}</h4>
</div>
<div>
<p id="author">{`- ${author}`}</p>
</div>
<div id="button">
<button onClick={() => setNewQuote(null)} className='bg-black text-white rounded-xl p-2 abs'>New Quote</button>
</div>
</div>
</div>
)
}
export default Main
Refactor the fetch logic into a callback that can be called from either the useEffect hook or directly in a button's onClick handler.
const Main = () => {
const [quote, setQuote] = useState({});
// Memoize a stable callback function reference
const fetchQuote = useCallback(() => {
fetch('https://type.fit/api/quotes')
.then(response => response.json())
.then((data) => {
const randomIndex = Math.floor((Math.random() * data.length));
setQuote(quotes[randomIndex]);
})
.catch(err => console.error(err));
}, []);
// Initial fetch of quotes
useEffect(() => {
fetchQuote();
}, [fetchQuote]);
if (!quote) return null;
return (
<div id='main' className='....'>
{/* Quote Gen Container */}
<div className='....'>
<h1 className='....'>Random Quote Generator</h1>
<div>
<h4 id="text">{`"${quote.text}"`}</h4>
</div>
<div>
<p id="author">{`- ${quote.author}`}</p>
</div>
<div id="button">
<button
onClick={fetchQuote} // <-- attach callback to fetch quote
className='....'>
New Quote
</button>
</div>
</div>
</div>
)
}
If the fetched data doesn't change then fetch it once and select a random quote.
const Main = () => {
const [quotes, setQuotes] = useState([]);
const [quote, setQuote] = useState(null);
// Memoize function to set single random quote
const getQuote = useCallback(() => {
const randomIndex = Math.floor((Math.random() * quotes.length));
setQuote(quotes[randomIndex]);
}, [quotes]);
// Mounting useEffect to fetch and save all quotes
// and set initial random quote
useEffect(() => {
fetch('https://type.fit/api/quotes')
.then(response => response.json())
.then((data) => {
setQuotes(data);
const randomIndex = Math.floor((Math.random() * data.length));
setQuote(data[randomIndex]);
})
.catch(err => console.error(err));
}, []);
if (!quote) return null;
return (
<div id='main' className='....'>
{/* Quote Gen Container */}
<div className='....'>
<h1 className='....'>Random Quote Generator</h1>
<div>
<h4 id="text">{`"${quote.text}"`}</h4>
</div>
<div>
<p id="author">{`- ${quote.author}`}</p>
</div>
<div id="button">
<button
onClick={getQuote}
className='....'
>
New Quote
</button>
</div>
</div>
</div>
)
}
Assuming you don’t need to re-fetch the data every time you could store the returned data in state, then have your onclick just choose a new random entry to set as the “current” quote.

Use State not updating as expected

Fairly new to react and trying to build a clone of The Movie Database site. I want this toggle switch to change my api call from movies to tv. It starts working after a couple clicks, but then it throws everything off and it's not displaying the correct items anyway. Not really sure what's going on here...or even why it starts working after two clicks. Anyone know whats up with this?
import React, { useState, useEffect } from "react";
import axios from "axios";
import API_KEY from "../../config";
const Popular = ({ imageUri }) => {
// GET POPULAR MOVIES
const [popularMovies, setPopularMovies] = useState("");
const [genre, setGenre] = useState("movie");
console.log(genre);
const getPopular = async () => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${genre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, []);
const listOptions = document.querySelectorAll(".switch--option");
const background = document.querySelector(".background");
const changeOption = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
getPopular();
listOptions.forEach((option) => {
option.classList.remove("selected");
});
el = el.target.parentElement.parentElement;
let getStartingLeft = Math.floor(
listOptions[0].getBoundingClientRect().left
);
let getLeft = Math.floor(el.getBoundingClientRect().left);
let getWidth = Math.floor(el.getBoundingClientRect().width);
let leftPos = getLeft - getStartingLeft;
background.setAttribute(
"style",
`left: ${leftPos}px; width: ${getWidth}px`
);
el.classList.add("selected");
};
return (
<section className="container movie-list">
<div className="flex">
<div className="movie-list__header">
<h3>What's Popular</h3>
</div>
<div className="switch flex">
<div className="switch--option selected">
<h3>
<a
data-genre="movie"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
In Theaters
</a>
</h3>
<div className="background"></div>
</div>
<div className="switch--option">
<h3>
<a
data-genre="tv"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
On TV
</a>
</h3>
</div>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies &&
popularMovies.map((movie, idX) => (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + "w500" + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export default Popular;
You're using the array index as your key prop when you're mapping your array.
You should use an id that is specific to the data that you're rendering.
React uses the key prop to know which items have changed since the last render.
In your case you should use the movie id in your key prop instead of the array index.
popularMovies.map((movie) => (
<div key={movie.id} className="card">
<div className="image">
<img src={imageUri + 'w500' + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
));
Also
You're calling the api directly after setGenre. However state changes aren't immediate. So when you're making your api call you're still sending the last movie genre.
Two ways of fixing this:
You could call your function with the genre directly, and change your function so it handles this case:
getPopular('movie');
Or you could not call the function at all and add genre as a dependency of your useEffect. That way the useEffect will run each time the genre change.
useEffect(() => {
getPopular();
}, [genre]);
PS: You should consider splitting your code into more component and not interacting with the DOM directly.
To give you an idea of what it could look like, I refactored a bit, but more improvements could be made:
const Popular = ({ imageUri }) => {
const [popularMovies, setPopularMovies] = useState('');
const [genre, setGenre] = useState('movie');
const getPopular = async (movieGenre) => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${movieGenre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, [genre]);
const changeHandler = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
};
const isMovieSelected = genre === 'movie';
const isTvSelected = genre === 'tv';
return (
<section className="container movie-list">
<div className="flex">
<MovieHeader>What's Popular</MovieHeader>
<div className="switch flex">
<Toggle onChange={changeHandler} selected={isMovieSelected}>
In Theaters
</Toggle>
<Toggle onChange={changeHandler} selected={isTvSelected}>
On TV
</Toggle>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies.map((movie) => {
const { title, id, poster_path } = movie;
return (
<MovieItem
title={title}
imageUri={imageUri}
key={id}
poster_path={poster_path}
/>
);
})}
</div>
</div>
</div>
</section>
);
};
export default Popular;
const Toggle = (props) => {
const { children, onChange, selected } = props;
const className = selected ? 'switch--option selected' : 'switch--option';
return (
<div className={className}>
<h3>
<a
data-genre="movie"
onClick={onChange}
className="switch--anchor"
>
{children}
</a>
</h3>
<div className="background"></div>
</div>
);
};
const MovieHeader = (props) => {
const { children } = props;
return (
<div className="movie-list__header">
<h3>{children}</h3>
</div>
);
};
const MovieItem = (props) => {
const { title, imageUri, poster_path } = props;
return (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + 'w500' + poster_path} />
</div>
<p>{title}</p>
</div>
);
};

React JS doesn't render on first time

import React, { useState, useRef, useEffect } from "react";
import fire from "../../../config";
import { useAuth } from "../../AuthContext";
import { Grid, Paper, Typography } from "#material-ui/core";
import "./style.css";
import { Link } from "react-router-dom";
import UserReviewComponent from "./UserReviewComponent";
import ReviewComponent from "../Reviews/ReviewComponent";
import ReactPaginate from "react-paginate";
export default function UserReviews() {
const [reviews, setReviews] = useState([]);
const [photo, setPhoto] = useState();
const [state, setstate] = useState();
const { currentUser } = useAuth();
const refItem = fire.firestore().collection("User");
const [loading, setLoading] = useState([true]);
const [vendorDetails, setVendorDetails] = useState([]);
const [error, setError] = useState(0);
const [value, setValue] = useState(0);
const [rating, setRating] = useState(0);
useEffect(() => {
fetchUserDetails();
fetchUserReviews();
}, []);
const [users, setUsers] = useState([]);
const [pageNumber, setPageNumber] = useState(0);
const usersPerPage = 2;
const pagesVisited = pageNumber * usersPerPage;
const pageCount = Math.ceil(users.length / usersPerPage);
const changePage = ({ selected }) => {
setPageNumber(selected);
};
const displayUsers = users
.slice(pagesVisited, pagesVisited + usersPerPage)
.map((v) => {
return (
<ReviewComponent
vendorid={v.vendorId}
rating={v.rating}
review={v.review}
useremail={v.useremail}
username={v.username}
userid={v.userid}
date={v.date}
id={v.id}
/>
);
});
const fetchUserReviews = () => {
fire
.firestore()
.collection("VendorReviews")
.where("useremail", "==", currentUser.email)
.get()
.then((querySnapshot) => {
const item = [];
querySnapshot.forEach((doc) => {
item.push(doc.data());
});
setReviews(item);
setUsers(reviews.slice(0, 50));
});
};
const fetchUserDetails = () => {
refItem.doc(currentUser.email).onSnapshot((doc) => {
if (doc.exists) {
setstate(doc.data().status);
setPhoto(doc.data().photourl);
} else {
console.log("No such document!");
}
});
};
// const getTotalUserRating = () => {
// let totalRating = 0;
// reviews.map((v) => {
// totalRating += v.rating;
// });
// setRating(totalRating);
// setLoading(false);
// };
// if (loading) {
// return <div className="App">Loading...</div>;
// }
return (
<>
<div className="container-1">
<div className="row">
<div className="col-md-12">
<div id="content" className="content content-full-width">
<div className="profile-1">
<div className="profile-header">
<div className="profile-header-cover"></div>
<div className="profile-header-content">
<div className="profile-header-img">
<img src={photo} alt="" />
</div>
<div className="profile-header-info">
<h4 className="m-t-10 m-b-5">
{currentUser.displayName}
</h4>
<p className="m-b-10">{state}</p>
<Link
to={`/user/${currentUser.uid}`}
className="btn btn-sm btn-info mb-2"
>
Edit Profile
</Link>
</div>
</div>
<ul className="profile-header-tab nav nav-tabs">
<li className="nav-item">
<a
href="#profile-post"
className="nav-link active show"
data-toggle="tab"
>
My Reviews
</a>
</li>
</ul>
</div>
</div>
<div className="container">
<div className="col-md-12">
<div className="offer-dedicated-body-left">
<div className="tab-content" id="pills-tabContent">
<div
className="tab-pane fade active show"
id="pills-reviews"
role="tabpanel"
aria-labelledby="pills-reviews-tab"
>
<div className="bg-white rounded shadow-sm p-4 mb-4 restaurant-detailed-ratings-and-reviews">
<h5 className="mb-1">All Ratings and Reviews</h5>
{displayUsers}
<hr />
<hr />
<a
className="text-center w-100 d-block mt-4 font-weight-bold"
href="#"
>
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationBttns"}
previousLinkClassName={"previousBttn"}
nextLinkClassName={"nextBttn"}
disabledClassName={"paginationDisabled"}
activeClassName={"paginationActive"}
/>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}
If I edit something on the IDE and then save and then the content appears but there is nothing on the first render. Please help me out. I think its due to pagination and the array has undefined values on the first render and so it returns nothing. The pagination slices the array so that the pagination is implemented.
add following in useEffects dependency array
useEffect(() => {
fetchUserDetails();
fetchUserReviews();
}, [reviews, state, photo, users ]);

Update className with UseEffect hook

Currently i am practising my React skills. Now i am working on my Hooks knowledge. I have used a Coctail API to load some data. Just for fun i divided this up into three categories with a navigation bar.
When i click on the navigation items i go to the subcategories. But my classes won't update so the tab system doesn't work. You guys know what i have to do here? I tried this in a few different ways but none worked.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const OverView = () => {
const [ term, setTerm ] = useState('Gin');
const [ categorie, setCategorie ] = useState('Cocktail');
const [ debouncedTerm, setDebouncedTerm ] = useState(term);
const [ results, setResults] = useState([]);
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedTerm(term);
}, 10);
return () =>{
clearTimeout(timerId);
};
}, [term]);
useEffect(() => {
const search = async () => {
const { data } = await axios({
'method':'GET',
'url':'https://the-cocktail-db.p.rapidapi.com/filter.php',
'headers':{
'content-type':'application/octet-stream',
'x-rapidapi-host':'the-cocktail-db.p.rapidapi.com',
'x-rapidapi-key':'49adf3a5ccmsh5d525b0005370d3p1b460bjsn7b0add17579e',
'useQueryString':true
},'params':{
'i': debouncedTerm
},
});
setResults(data.drinks);
};
search();
}, [debouncedTerm]);
const renderResults = results.map((result) => {
return (
<div key={result.idDrink} className="four wide column" style={{marginBottom:"20px"}}>
<a href={result.idDrink}>
<div className="card">
<img src={result.strDrinkThumb} width="250"/>
<div className="header">{result.strDrink}</div>
</div>
</a>
</div>
);
});
return (
<div className="ui grid">
<div className="row">
<h3>Select your favourate drink to get started</h3>
<div className="ui top attached tabular menu">
<div
className={'item active'}
onClick={(e) => setTerm('Gin') }>
Gin
</div>
<div
className={'item'}
onClick={(e) => setTerm('Vodka')}>
Vodka
</div>
<div
className={'item'}
onClick={(e) => setTerm('Whiskey')}>
Whiskey
</div>
</div>
</div>
<div className="row">
{renderResults}
</div>
</div>
);
};
export default OverView;
Thanks in advance.My first problem is that i don't know how to add an extra action upon my hook. I can't attach an extra onClick event and don't know how to add this to my useEfect functions.
Set the className based on the term state atom, simple as that.
I also refactored things a bit:
the result component is now, well, a separate component
searching is refactored into a separate function
import React, { useState, useEffect } from "react";
import axios from "axios";
const ResultCard = ({ result }) => (
<div className="four wide column" style={{ marginBottom: "20px" }}>
<a href={result.idDrink}>
<div className="card">
<img src={result.strDrinkThumb} width="250" />
<div className="header">{result.strDrink}</div>
</div>
</a>
</div>
);
async function doSearch(term) {
const { data } = await axios({
method: "GET",
url: "https://the-cocktail-db.p.rapidapi.com/filter.php",
headers: {
"content-type": "application/octet-stream",
"x-rapidapi-host": "the-cocktail-db.p.rapidapi.com",
"x-rapidapi-key": "49adf3a5ccmsh5d525b0005370d3p1b460bjsn7b0add17579e",
useQueryString: true,
},
params: {
i: term,
},
});
return data;
}
const OverView = () => {
const terms = ["Gin", "Vodka", "Whiskey"];
const [term, setTerm] = useState("Gin");
const [results, setResults] = useState([]);
useEffect(() => {
doSearch(term).then((data) => setResults(data.drinks));
}, [term]);
return (
<div className="ui grid">
<div className="row">
<h3>Select your favourate drink to get started</h3>
<div className="ui top attached tabular menu">
{terms.map((t) => (
<div
className={["item", term === t ? "active" : null].filter(Boolean).join(" ")}
onClick={(e) => setTerm(t)}
>
{t}
</div>
))}
</div>
</div>
<div className="row">
{results.map((result) => (
<ResultCard result={result} key={result.idDrink} />
))}
</div>
</div>
);
};
export default OverView;
You may want to look into the classnames module; the arcane [].filter().join() expression would become cx({item: true, active: t === term}) :)

Resources