Fetching data before opening modal in React - javascript

Thanks for checking this post.
I'm new to React and I have a question about fetching data before opening a modal.
I have a card list and when I click on a card, a modal pops up with the data of clicked item (handleModal function takes id of clicked item). And I want the data inside already without delay every time the modal pops up.
const [eachUser, setEachUser] = useState({});
const [isModalOn, setIsModalOn] = useState(false);
const loadDetailInfo = async id => {
if (isModalOn) {
return;
}
await axios
.get(`${API}/api/users/${id}`)
.then(res => setEachUser(res.data.data))
.catch(err => console.log(err));
};
const handleModal = id => {
if (!isModalOn) {
setEachUser('');
}
loadDetailInfo(id);
setIsModalOn(!isModalOn);
};
return (
<>
<CardList data={filteredUser} handleModal={handleModal} />
{isModalOn && <Modal data={eachUser} handleModal={handleModal}/>}
</>
);
But with this code, since it takes time to fetch data from the API, modal shows blank view at first and I don't know how to fix this.
I've tried useEffect and putting the loadDetailInfo function inside with the id state in a dependency array but I couldn't fix it for some reason..
It would be really appreciate if you give me some advice.Thank you for your time and advice in advance.

Related

how to detect if user is typing in react native?

I have an application in react native where i'm developing a search feature like Instagram.
It is like if user stop typing show him his query result.
my current approach is messing up redux. And sometimes it returns same element multiple times or sometime random elements which are irrelevant of that query.
right now. I'm calling search api immediately as use start typing in searchbar.
here is code below of my component.
import { getSearchDataApi } from "../../api/search/search";
import { clearSearchData, setSearchData } from "../../redux/action/search";
const SearchScreen =(props)=>{
const [autoFocus,setAutoFocus] = useState(true)
const [keyWord,setKeyWord] = useState(null)
const [isLoading,setIsLoading] = useState(false)
const [isError,setIsError] = useState(false)
const [pageNumber,setPageNumber] = useState(1)
const [loadMore,setLoadMore] = useState(true)
const loadMoreDataFunc =()=>{
if (pageNumber <= props.totalSearchPage) {
setPageNumber(pageNumber+1)
}
else {
setLoadMore(false)
}
}
const searchData = async(keyWord)=>{
console.log(keyWord,pageNumber)
try {
setIsLoading(true)
var searchResponse = await getSearchDataApi(keyWord,pageNumber)
props.setSearchData(searchResponse.data)
setIsLoading(false)
}
catch (e) {
setIsError(true)
console.log("Error --- ", e.response.data.message)
showMessage({
message: e.response.data.message,
type: "danger",
});
}
}
return (
<View>
....
</View>
)
}
const mapStateToProps = (state)=>({
searchData: state.searchReducer.searchData,
totalSearchPage: state.searchReducer.totalSearchPage,
})
export default connect(mapStateToProps,{setSearchData,clearSearchData})(SearchScreen);
I will really every thankful to someone how can help me in fixing. Appreciation in advance!
GOAL :
The goal that i want to achieve is when user stop typing then i call searchAPI with the keyword he/she entered in searchBar that's all.
I have also tried setTimeOut but that made things more worse.
The best solution to your problem is to debounce the state variable that is responsible for the user input. This way, you can use the effect hook to watch for changes on the debounced variable, and call the search API if/when conditions for the search API variables are met.
Well, I have put some effort to solve it with setTimeout once again and i have done it by following code of snippet.
useEffect(()=>{
setPageNumber(1)
props.clearSearchData()
const delayDebounceFn = setTimeout(() => {
console.log(keyWord)
if (keyWord) {
searchData(keyWord)
}
}, 500)
return () => clearTimeout(delayDebounceFn)
},[keyWord])
You can use a setInterval to create a countDown starting from 2 to 0, or 3 to 0, put it a state.
whenever user types, onChange is called, the from the callback you reset the countDown.
using useEffect with the countDown as dependency, you can open the search result whenever the countdown reaches 0. (which means the user hasn't typed anything since 2s ago)
this might help for creating the countdown https://blog.greenroots.info/how-to-create-a-countdown-timer-using-react-hooks

ReactJS and Azure library problem with displaying certain elements

so I'm doing a school project where I'm trying to fetch data from my Azure blob-storage and then displaying it on a website which is creating in ReactJS. I've managed to fetch the pictures from my storage and they render fine, but when I try to render the "createdOn prop" nothing is being shown.
I just wanna add that I'm fairly new to javascript so my code might look like garbage.
The BodyPresenter function is the one that connects to my blob storage and then retrieves the data. It works without a problem for the "blobImages" but won't work for the "blobDateTime"
function BodyPresenter() {
const [blobImages, setBlobImages] = useState([]);
const [blobDateTime, setBlobDateTime] = useState([]);
const account = "ktodb";
const containerName = "images";
useEffect(() => {
async function blobStorage() {
const blobServiceClient = new BlobServiceClient(api_client.get_blob_account(account));
const containerClient = blobServiceClient.getContainerClient(containerName);
let blobs = containerClient.listBlobsFlat();
let newArrayForImages = [];
let newArrayforDates = [];
for await (const blob of blobs) {
// console.log(`${blob.name}`); //`Blob ${i++}:
newArrayForImages.push(blob.name);
newArrayforDates.push(blob.properties.createdOn)
}
setBlobImages(newArrayForImages);
setBlobDateTime(newArrayforDates);
}
return blobStorage;
}, [blobImages])
console.log(blobDateTime)
As you can see I console.log() the blobDateTime at the bottom and it gives me the data I want in the console, aka the creation data of all my images:
image of console
Then I try to render the data in a view file and I guess the problem lies here, but I'm sure what the problem is and how to fix it, to remind you I'm a new to javascript. The images are being rendered but not the DateTime
function BodyView( {blobImages, blobDateTime} ) {
console.log(blobDateTime)
return (
<div>
{blobImages.map( (blobImages, blobDateTime) => (
<div>
<div className="images">
<img
key={blobImages.id}
src={api_client.get_image_url(blobImages)}
alt=""
/>
</div>
<div>{blobDateTime}</div>
</div>
))}
</div>
)
}

How to use setState to render individual data returned from API call using map

How do I use setState to render individual data returned from API call. As the question implies... What I am getting instead is another array of data.
Here's the code:
const [likes, setLikes] = useState(0);
useEffect( async () => {
const query = await getDevsApi(); //This returns a collection of data I can map.
const likes = query.map(like => like.upvotes); //The problem here is, it returns an array of data... How do I get individual data for each user? and set it at as the current state in the setLikes() below.
setLikes(likes)
},[]);
I am still wrapping my head around reactjs. Can anyone explain the best approach for me?
How it looks...
UPDATED CODE
ver. 1.
const [likes, setLikes] = useState(0);
useEffect(() => {
//using an anonymous async function inside a useEffect
( async () => {
const query = await getDevsApi();
setLikes(query.map(like => like.upvotes));
})
(); //calling the anonymous function.
},[]);
ver. 2.
const [likes, setLikes] = useState(0);
useEffect(() => {
( async () => {
const query = await getDevsApi();
const likes = query.map(like => like.upvotes);
setLikes(likes);
})
();
},[]);
However, they both produce the same result.
The idea is to get the number of likes from the API and display them in this section
<span className={styles.iconLeft}>
<a href="#" onClick={addLike} ><small>{likes} Likes <FontAwesomeIcon className={styles.columnIcon} icon={faHeart} /></small> </a>
</span>
The {likes} is where the magic happens. The initial state was set to 0... and the useEffect will be used to display the real data gotten from the API. And Yes, I got the data BUT it's not distributed to each user as supposed, rather it's giving me an array and appending it to all the user.
See the screenshot above.
I hope I'm able to communicate clearly though.
I just pushed my project to vercel.
here is a branch of what I am having now... https://naijadev-4g9llmolb-badt0men.vercel.app/
And here is another branch of how I want it... https://naijadev-five.vercel.app
And yes this is my very first attempt working with Jamstack... using the now popular headless CMS (strapi)
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array. see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
But this won't be an issue. if you want to render individual data from an array you should use the map() method in the return block of a functional component where UI is rendered.
const [likes, setLikes] = useState([]);
useEffect( async () => {
const query = await getDevsApi();
const likes = query.map(like => like.upvotes);
setLikes(likes)
},[]);
return (
...
<span className={styles.iconLeft}>
{ likes?.map(like => <a href="#" onClick={addLike} ><small>{like} Likes
<FontAwesomeIcon className={styles.columnIcon} icon={faHeart} /></small> </a>
}
</span>
)

Cannot read property of undefined - small random quote app bug

I've been trying to build a random quote app. So far I have this: https://codesandbox.io/s/nice-heyrovsky-4msq4?file=/src/App.js
In short, I fetch a quotes list from an API, and then try to display the quotes and the authors when the page loads, and every time I press the button. But there's a problem.
export default function App() {
useEffect(() => {
fetch(
"https://gist.githubusercontent.com/natebass/b0a548425a73bdf8ea5c618149fe1fce/raw/f4231cd5961f026264bb6bb3a6c41671b044f1f4/quotes.json"
)
.then((data) => data.json())
.then((quotes) => setAllQuotes(quotes));
}, []);
const [allQuotes, setAllQuotes] = useState([]);
// this is the array of all quote objects
const [quoteIndex, setQuoteIndex] = useState(0);
//this is the number used as array index
const [text, setText] = useState("This app is Broken");
// this is the quote text to be displayed
const [author, setAuthor] = useState("Press set twice");
// this is the quote author to be displayed
const [chosenQuote, setChosenQuote] = useState({});
// this is the selected quote object
function randomNumber() {
return Math.floor(Math.random() * allQuotes.length);
}
let handleClick = () => {
setQuoteIndex(randomNumber);
setText(chosenQuote.quote);
setAuthor(chosenQuote.author);
setChosenQuote(allQuotes[quoteIndex]);
// this is supposed to choose a random object within the array, and set text and quote state to the quote and author properties of the selected object
};
console.log(chosenQuote);
return (
<div id="quote-container">
<div id="quote-box">
<h1 id="text">{text}</h1>
<p id="author">{author}</p>
{/*when I press the button, it loads the next quote, but displays the previous quote, hence why nothing displays on first click */}
<button onClick={handleClick}>New Quote</button>
</div>
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
If I try to load the quotes while the page loads, it gives me the "Cannot read property(quote) of undefined" error. If I try to load the quotes after the page has loaded by editing the javascript, then it works, but it displays the messages from the previously loaded object, not the current one. You can see the behavior in codesandbox.
So, can someone explain to me why this is happening and how I can fix it so that the app displays the quotes properly on load and then changes on click?
this is because setState from useState hook is asynchronous so you can do something like:
setChosenQuote(allQuotes[quoteIndex]);
setText(allQuotes[quoteIndex].quote);
setAuthor(allQuotes[quoteIndex].author);

How to access state values from a different class in React?

I'm trying to create a search bar that filters out a set of data. The search function I made uses several states to filter results. When the search bar and results page are in the same class, the search function works but what I'm trying to do now is separate the search bar and display the search results on a separate page. Here's the state being set in the SearchBar class.
handleChange = (event) => {
this.setState({
names: event.target.value
})
}
The problem is I have no idea how to get the data stored in the SearchBar class to be displayed on the results page. Here's how I'm filtering the results on the results page.
const filteredData = data.filter(entry => (entry.name === (this.state.names))
This data is being filtered in the Search class but this.state.names is being stored in the SearchBar class. This SearchBar class is being displayed on my header where users can search for whatever they want and after they press search, the results page appears. So how can I take the data stored in the SearchBar class and use it in a different class?
UPDATE: I tried passing in the state to the Search class in the render function but that causes the entire page to just freeze.
render() {
return (
<Search names = {this.state.names} />
)
}
Not sure if I understood correctly but:
You can make a new component to store your data.
Then use this function (or similar) in onChange on that component
const filterData = (e) => {
const valueToCheck = e.target.value
let newArr = []
for(entry of data) {
// do the logic
//push the data you want into an array
newArr.push(entry)
}
setState(newArr)
}
SearchBar should call onSearchResults([...]) callback and then PageResult may accept those results, you need a component that orchestrate all.
const App = () =>{
const [results, setResults] = useState([]);
return (<>
<SearchBar onSearchChange={setResults}/>
{ results.length && <PageResult results={results}/> }
</>)
}
SearchBar will call props.onSearchChange(results) with the filtered data. App component will react to that change and send those results to PageResult component

Categories