Add a Spinner while waiting for promise to resolve in react - reactjs

const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const getRandomMusic = () => {
return fetchMusic().then((result) => result.data);
};
const Button = (props) => {
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
<SomeComponent />
<p>Some text here</p>
</div>
);
};
I want add a spinner while waiting for the promise to resolve .
fetchMusic is in some other file.I m importing it in a component .

TLDR
How about use useState and useCallback for that action
Answer
At terms of react, use State for loading action is right use case.
So, When to start function, use can setLoading(true) and after action you can setLoading(false) for make loading effect
const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const Button = (props) => {
const [loaidng, setLoading] = useState(false);
const getRandomMusic = useCallback(() => {
setLoading(true)
return fetchMusic().then((result) => {
setLoading(false);
result.data
});
},[]);
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
{loading && <Sipinner/>}
<SomeComponent />
<p>Some text here</p>
</div>
);
};
Reference
Example of loading
ETC
If you have any other question. Just give me comment please.

Related

React setState returns function dispatch()

I have met issues setting the state of the const displayClipName in the following function which is blocking despite the fact that I passed the props from the parent element to the child.
const audioClips = [
{
keyCode: 67,
keyTrigger: "C",
id: "Closed-HH",
src: "https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3"
}
]
function App() {
const [displayClipName, setDisplayClipName] = React.useState('Click a key!')
return (
<div id="drum-machine" className="text-white text-center">
<div className="container bg-info">
<h1>FCC - Drum Machine</h1>
{audioClips.map((clip) => (
<Pad
key={clip.id}
clip={clip}
setDisplayClipName={setDisplayClipName}
/>
))}
<h2>{displayClipName}</h2>
</div>
</div>
)
}
const Pad = ({clip, setDisplayClipName}) => {
const [playing, setPlaying] = React.useState(false)
React.useEffect(() => {
document.addEventListener('keydown', handleKey);
return () => {
document.removeEventListener('keydown', handleKey)
}
}, [])
const handleKey = (e) => {
if(e.keyCode === clip.keyCode) {
playSound()
}
}
const playSound = () => {
const audioPlay = document.getElementById(clip.keyTrigger);
const clipName = document.getElementById(clip.id)
setPlaying(true);
setTimeout(() => setPlaying(false), 300);
audioPlay.currentTime = 0;
audioPlay.play();
setDisplayClipName(clipName);
console.log(setDisplayClipName)
}
return (
<div onClick={playSound} id={`drum-pad-${clip.keyTrigger}`}>
<audio src={clip.src} className="clip" id={clip.keyTrigger}/>
{clip.keyTrigger}
</div>
)
}
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);
The console returns the following message:
function dispatchSetState()
​
length: 1
​
name: "bound dispatchSetState"
​
<prototype>: function ()
As some have pointed out in comments to your post it'd be better if you used refs. Also you were logging a function that's why the console displayed that. I have taken the liberty to do some modifications to your code so I could understand it better, I would suggest you keep the ones you find reasonable:
The displayName variable has an undefined state when no song is playing. This could be set from any other part of the application but you wouldn't depend on rerendering the component for it to return to a default message ("Press a key!")
The playSound function could be bound to the id of the song and you would avoid having to check the HTML element that received the input.
Here is a working snippet.
const { useState, useEffect } = React;
const audioClips = [
{
keyCode: 67,
keyTrigger: "C",
id: "Closed-HH",
src: "https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3"
}
]
const Pad = ({ clip, setDisplayClipName }) => {
const [playing, setPlaying] = useState(false);
useEffect(() => {
document.addEventListener("keydown", handleKey);
return () => {
document.removeEventListener("keydown", handleKey);
};
}, []);
const handleKey = (e) => {
if (e.keyCode === clip.keyCode) {
playSound(clip.id);
}
};
const playSound = (clipId) => {
const audioPlay = document.getElementById(clip.keyTrigger);
setPlaying(true);
setTimeout(() => setPlaying(false), 300);
audioPlay.currentTime = 0;
audioPlay.play();
setDisplayClipName(clipId);
};
return (
<div onClick={() => playSound(clip.id)} id={`drum-pad-${clip.keyTrigger}`}>
<audio src={clip.src} className="clip" id={clip.keyTrigger} />
{clip.keyTrigger}
</div>
);
};
function App() {
const [clipName, setClipName] = useState(undefined);
return (
<div id="drum-machine" className="text-white text-center">
<div className="container bg-info">
<h1>FCC - Drum Machine</h1>
{audioClips.map((clip) => (
<Pad key={clip.id} clip={clip} setDisplayClipName={setClipName} />
))}
<h2>{clipName ? clipName : "Press a key!"}</h2>
</div>
</div>
);
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
To further refine the Pad component and avoid the missing dependencies on the useEffect hook I would suggest you model it like this, using useMemo:
export const Pad = ({ clip, setDisplayClipName }) => {
const [playing, setPlaying] = useState(false);
const playSound = useMemo(
() => () => {
const audioPlay = document.getElementById(clip.keyTrigger);
setPlaying(true);
setTimeout(() => setPlaying(false), 300);
if (audioPlay) {
audioPlay.currentTime = 0;
audioPlay.play();
}
setDisplayClipName(clip.id);
},
[setDisplayClipName, clip]
);
useEffect(() => {
const handleKey = (e) => {
if (e.keyCode === clip.keyCode) {
playSound();
}
};
document.addEventListener("keydown", handleKey);
return () => {
document.removeEventListener("keydown", handleKey);
};
}, [playSound, clip.keyCode]);
return (
<div onClick={playSound} id={`drum-pad-${clip.keyTrigger}`}>
<audio src={clip.src} className="clip" id={clip.keyTrigger} />
{clip.keyTrigger}
</div>
);
};

React useState async setter doesn't update value passed as props

I have this component in my React project -
const ViewPost = (props: Props) => {
const [listingData, setListingData] = useState<any>({})
const [auctionData, setAuctionData] = useState<any>({})
useEffect(() => {
if (props.listingId) {
getListingData()
}
}, [props.listingId])
const getListingData = async () => {
const { data } = await getListingById(props.listingId)
setListingData(data?.data)
if (data.data.isTimedAuction) {
auctions(data.data.auctionId)
}
}
const auctions = async (auctionId: any) => {
const auction = await getAuctions(auctionId)
console.log('auction', auction.data)
setAuctionData(auction.data)
}
return (
<>
<Navbar />
<div className={classes.viewPostPage}>
<div className={classes.bodyContainer}>
<Details
data={listingData as any}
updateListing={getListingData}
auctionData={auctionData}
/>
</div>
</div>
</>
)
}
export default ViewPost
Basically, I'm getting data from an API and assigning it to auctionData.
console.log(auction.data) shows me the desired result but when I pass auctionData as props into Details I get an empty object which leads to a lot of issues, since useState is async.
How can I overcome this problem?
const [auctionData, setAuctionData] = useState<any>({})
your default value is an empty object, that causes the problems.
should set null or undefined as default value, and hide the Details when not have the data.
Use loading state. Once data is fully fetched from api then pass to child component. I think what is happeing here is that child component is called with empty state variable while data is still being fetched.
const [isLoading, setIsLoading] = useState(true)
const getListingData = async () => {
const { data } = await getListingById(props.listingId)
.then((data) => {setListingData(data)})
.then((data) => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
})
if (data.data.isTimedAuction) {
auctions(data.data.auctionId)
}
}
and then return
if (isLoading) {
return (
<div>
Loading...
</div>
)
}
return (
<>
<Navbar />
<div className={classes.viewPostPage}>
<div className={classes.bodyContainer}>
<Details
data={listingData as any}
updateListing={getListingData}
auctionData={auctionData}
/>
</div>
</div>
</>
)
}

How optimize little code React "RandomImage" from array with onclick

I make a little code for take array images and show one and change it with click, but I think that code can be more optimized, especialy the second useState but I dont know how.
import React, { useEffect, useState } from 'react';
const apiURL = 'https://picsum.photos/v2/list?page=2&limit=100';
export default function Image() {
function random(mn, mx) {
return Math.random() * (mx - mn) + mn;
}
const [gifs, setgifs] = useState([]);
useEffect(function () {
console.log('test');
fetch(apiURL)
.then((res) => res.json())
.then((response) => {
const gifs = response.map((image) => image.download_url);
setgifs(gifs);
});
}, []);
let imagenaleatoria = gifs[Math.floor(random(1, gifs.length))];
const [imagenactual, nuevaimagen] = useState();
return (
<div className='App'>
<section className='App-header'>
<div className='caja'>
<img
onClick={() => {
nuevaimagen(gifs[Math.floor(random(1, gifs.length))]);
}}
src={imagenaleatoria}
></img>
</div>
</section>
</div>
);
}
Thanks so much.
I thought I just rewrite your code snippet and add comments so you understand what and why
import { useState, useEffect } from "react";
const apiURL = "https://picsum.photos/v2/list?page=2&limit=100";
// has no acces to state so there is no need to create this funtion isde the RandomImage component
export const getRandomIndex = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
};
export const RandomImage = () => {
const [gifs, setGifs] = useState([]);
const [randomIndex, setRandomIndex] = useState(null);
// runs only after the first render
useEffect(function () {
console.log("test");
fetch(apiURL)
.then((res) => res.json())
.then((response) => {
const gifs = response.map((image) => image.download_url);
setGifs(gifs);
setRandomIndex(getRandomIndex(0, gifs.length));
});
}, []);
// just sets an random index
const handleClick = (event) => {
setRandomIndex(getRandomIndex(0, gifs.length));
};
return (
<div className="App">
<section className="App-header">
<div className="caja">
{!gifs.length ? (
'Loading...'
) : (
<img onClick={handleClick} src={gifs[randomIndex]} />
)}
</div>
</section>
</div>
);
};

How to edit data received from a rest call in react?

I am trying to make a rest call, edit the data and then render it. The problem is that, while editing, I am getting a error - undefined even after checking if the data is there.
component I am making the rest call from:
function Header ({timesheetData, loadTimesheet}) {
useEffect(() => {
loadTimesheet(date)
}, [])
return(
<>
<header className="header">
<div className="wrap">
<span className="btn-icon">
<IconPlus onClick={() => setIsOpen(true)} className="icon icon-plus js-modal-init"/>
</span>
<div className="header-blockquote">
<h1 className="header-quote">{currentQuote.quote}</h1>
<div className="header-cite">{currentQuote.author}</div>
</div>
</div>
<div className="header-inner">
<div className="wrap">
<VegaIcon className="logo" alt="VegaIT"/>
<div className="date-wrap">
<IconCalendar className="icon icon-plus js-modal-init"/>
//
<time>{timesheetData.timesheet.loading ? "asd" : edit(timesheetData) }</time>
//
</div>
</div>
</div>
</header>
</>
)
}
function edit(timesheetData){
let newDate = timesheetData.timesheet.date
newDate = newDate.split("-")
newDate = newDate.reverse()
return newDate.join("/")
}
the redux action:
export const loadTimesheet = (date) => {
let url = "http://localhost:8080/api/timesheet/" + date
return (dispatch) => {
dispatch(getTimesheet)
axios.get(url)
.then((response) => {
const timesheet = response.data
dispatch(getTimesheetSuccess(timesheet))
})
.catch(error => {
const errorMsg = error.message
dispatch(getTimesheetFailure)
})
}
}
Edit: added my mapStateToProps and mapDispatchToProps
const mapStateToProps = state => {
return {
timesheetData: state.timesheet
}
}
const mapDispatchToProps = dispatch => {
return {
loadTimesheet: (date) => dispatch(loadTimesheet(date))
}
}
Edit2: The code: https://codesandbox.io/s/adoring-tharp-o9ibe
use mapStateToProps, mapDispatchToProps:
import getTimesheet from '../actions/...'
Header ({timesheetData, loadTimesheet}) => {
}
const mapDispatchToProps = dispatch => {
return {
loadTimesheet: () => dispatch(getTimesheet()),
}
}
const mapStateToProps = (state) => {
return { timesheetData: state.timesheetData };
};
export default connect(mapStateToProps, mapDispatchToProps)(Header);
also in component:
before render:
check that you have:
timesheetData - not undefined
timesheetData.timesheet - not undefined
timesheetData.timesheet.loading - not undefined
const hasData = timesheetData && !timesheetData.timesheet.loading;
const time = hasData ? edit(timesheetData): "asd";
in render:
<time>{time}</time>

How do you prevent the component from disappearing too soon?

const FlashMessage = (props) => {
const [isOpen, setIsOpen] = useState(true);
const hideComponent = () => {
setisOpen(false);
};
useEffect(() => {
setIsOpen(true);
setTimeout(() => hideComponent(), 9000);
}, [props]);
return (
(props.flashMessage === true && isOpen) ?
<View style={styles.main}>
<Text style={styles.message}>{props.message}</Text>
</View>
: null
);
}
I have this Flash Message component in my React Native app, and sometimes, the Flash Message disappears after 2 second. It seems to appear on a random basis and it's probably due to a problem with useEffect and setTimeout, but I have not been able to figure out what might be causing this.
The effect you have with [props] as dependency doesn't make sense to me.
But you can have an isolated effect for the isOpen boolean.
useEffect(() => {
setTimeout(() => {
setIsOpen(false);
}, 9000);
}, [isOpen]);
Here is a full working example, simplified:
export default function App() {
const [show, setShow] = useState(false);
useEffect(() => {
setTimeout(() => {
setShow(false);
}, 2000);
}, [show]);
return (
<div className="App">
<button onClick={e => setShow(true)}>Show</button>
{show && <div>hello</div>}
</div>
);
}

Resources