Why is my useEffect hook updating too late? - javascript

I am trying to have an image and text update when I click a button, which it does, but the image is from the previous state. Is there a way to have it so that it changes instantly? Any help is appreciated. Using React btw.
function Footer({ spotify }) {
const [{ item, playing }, dispatch] = useDataLayerValue();
useEffect(() => {
spotify.getMyCurrentPlaybackState().then((r) => {
dispatch({
type: "SET_PLAYING",
playing: r.is_playing,
});
dispatch({
type: "SET_ITEM",
item: r.item,
});
});
}, [spotify]);
const handlePlayPause = () => {
if (playing) {
spotify.pause();
dispatch({
type: "SET_PLAYING",
playing: false,
});
} else {
spotify.play();
dispatch({
type: "SET_PLAYING",
playing: true,
});
}
};
return (
<div className='footer'>
<div className="footer__left">
<img className="footer__albumLogo"
src={item?.album.images[0].url}
alt={item?.name}
key={item?.album.images[0].url}/>
{item ? (
<div className="footer__songInfo">
<h4>{item.name}</h4>
<p>{item.artists.map((artist) => artist.name).join(", ")}</p>
</div>
) : (
<div className="footer__songInfo">
<h4>No song is playing</h4>
<p>...</p>
</div>
)}
</div>
// NEW EDIT
<div className="footer__center">
<ShuffleIcon className="footer__green" />
<SkipPreviousIcon className="footer__icon" />
{playing ? (
<PauseCircleOutlineIcon
onClick={handlePlayPause}
fontSize="large"
className="footer__icon"
/>
) : (
<PlayCircleOutlineIcon
onClick={handlePlayPause}
fontSize="large"
className="footer__icon"
/>
)}
<SkipNextIcon className="footer__icon" />
<RepeatIcon className="footer__green" />
</div>
</div>
)
}
export default Footer
I think that the issue is within the "div className="footer__left"" but it may be an entirely different issue.

Related

two React Componets in One section we upload image and can increase the number of input other section we will preview those images

I am working on a page
have two sections. In one section we upload image and can increase the number of input, in the other section we will preview the image.
I don't know I am not getting the right id that I want to edit.
we can add more fields on click of button and remove then at the end post data at api endpoint?
RightSection
const RightSection = ({ inputImage, setInputImage }) => {
const handleAddFields = () => {
if (inputImage.length <= 9) {
const values = [...inputImage];
values.push({
id: values.length + 1,
name: `Drop Image ${values.length + 1} Here`,
});
setInputImage(values);
}
};
const handleRemoveFields = () => {
if (inputImage.length > 3) {
const values = [...inputImage];
values.splice(values.length - 1, 1);
setInputImage(values);
}
};
const handleInputChange = (id, event) => {
console.log(id, event.target.id, "=====");
const newInputFields = inputImage.map((i) => {
if (id === i.id) {
i.url = URL.createObjectURL(event.target.files[0]);
i.name = event.target.files[0].name;
// push image object in array
setInputImage([
...inputImage,
{
id: id,
url: URL.createObjectURL(event.target.files[0]),
name: event.target.files[0].name,
},
]);
}
return i;
});
setInputImage(newInputFields);
};
console.log(inputImage);
return (
<>
<div id="right" className="flex">
<div className="margin">
<div className="inlineflex">
<H1>Background Image</H1>
<div>
<AddIcon onClick={handleAddFields} />
<RemoveIcon onClick={handleRemoveFields} />
</div>
</div>
</div>
<div
style={{
margin: "0 auto",
position: "relative",
width: "80%",
}}
>
{inputImage.map((inputField, index) => (
<FileInput key={index}>
<label
htmlFor={inputField.id}
onClick={(e) => {
console.log("click", index + 1);
}}
>
{inputField.name}
</label>
<input
id={index + 1}
onChange={(event) => handleInputChange(inputField.id, event)}
accept="image/*"
type="file"
/>
</FileInput>
))}
</div>
</div>
</>
);
};
LeftSection
const LeftSection = ({ inputImage }) => {
return (
<>
<div id="left" className="flex">
<div className="margin">
<H1>Preview</H1>
</div>
<Grid>
{Array.isArray(inputImage) &&
inputImage.map((item, index) => {
if (item?.url?.includes("http") || item?.url?.includes("https")) {
return (
<div key={index}>
<img src={item?.url} alt={item?.name} />
</div>
);
}
})}
</Grid>
</div>
</>
);
};
BackgroundImage
let initaValue = [
{ id: 1, name: "Drop Image 1 Here", url: "" },
{ id: 2, name: "Drop Image 2 Here", url: "" },
{ id: 3, name: "Drop Image 3 Here", url: "" },
];
const BackgroundImage = () => {
const [inputImage, setInputImage] = useState(initaValue);
return (
<>
<Container>
<RightSection inputImage={inputImage} setInputImage={setInputImage} />
<LeftSection inputImage={inputImage} setInputImage={setInputImage} />
</Container>
</>
);
};
export default BackgroundImage;
I think there is some issue with the handleInputChange function on the RightSection component.
Somehow I am not able to update the item with the correct id in the array.
Is there any other efficient solution for this problem?

why is my component.render() is in a loop?

I am building a simple weather app where I set a place to display the weather forcast with a boolean to control if it will show or not
<div className="column">
{this.state.displayResult ? <WeatherResult /> : null}
</div>
There the displayResult boolean will set to true by the handleSubmit() in the form and the fetchFavWeather() on the buttons, and will set to be false by HandleInputChange() in the first control class
async getCoord() {
let city = {
cityname: this.state.postcodeInput
}
axios.post('http://localhost:4001/search-location', city)
.then((response) => {
console.log(response);
this.setState({
displayResult: true
});
}, (error) => {
console.log(error);
});
}
handleSubmit(e) {
e.preventDefault();
this.getCoord();
}
handleInputChange(e) {
this.setState({
postcodeInput: e.target.value,
displayResult: false
});
}
fetchFavWeather(city){
this.setState({
displayResult: false,
postcodeInput: city
},()=>{
console.log("passing fav to forcast" + this.state.postcodeInput);
this.getCoord()
});
}
fetchFavCities(){
axios.get('http://localhost:4001/favouriteCites')
.then((res)=>{
this.setState({
favCts: res.data
})
});
}
render() {
this.fetchFavCities();
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<div className="column">
{
this.state.favCts.map(
(item, index) => <button key={index} onClick = {() => {this.fetchFavWeather(item)}}>{item}</button>
)}
</div>
<div className="control">
<input className="input" type="text" placeholder="input city here" onChange={this.handleInputChange} required />
</div>
<div className="field">
<div className="control">
<input type='submit' className="button is-light is-large" value='Check Weather' />
<input type='submit' className="button is-light is-large" value='Save as Favourite' onClick = {this.saveAsFavourite}/>
</div>
</div>
</div>
</form>
<div className="column">
{this.state.displayResult ? <WeatherResult /> : null}
</div>
</div>
)
}
however in my WeatherResult Component class, I found out that the render() is being called in an infinite loop, any ideas why?
class WeatherResult extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTemp: '',
humidity: '',
cityName: '',
days: []
}
}
async fetchWeather() {
let response = await fetch('http://localhost:4001/weather');
await response.json().then(data => {
console.log(data);
this.setState({
currentTemp: data['currentConditions']['temp'] + '°C',
//humidity: data.main.humidity + '%',
cityName: data.address,
days: data.days
})
})
}
componentDidMount() {
this.fetchWeather();
}
render() {
//console.log("why is this looping?");
return (
<div>
<p>Current Conditions at {this.state.cityName}</p>
<p>Current temperature: {this.state.currentTemp}</p>
<p>Humidity: {this.state.humidity}</p>
<p>Location: {this.state.cityName}</p>
<div>
<p>Forcast</p>
<p>Date: {this.state.days['datetime']}</p>
<p>weatherType: {this.state.days['icon']}</p>
</div>
</div>
)
}
}
export default WeatherResult;
You are called a fetch method inside your render function which will cause an infinite update loop.
render() {
this.fetchFavCities();
Infinite loop will happen because you probably set these favorite cities in your state and reference them here
<div className="column">
{
this.state.favCts.map(
(item, index) => <button key={index} onClick = {() => {this.fetchFavWeather(item)}}>{item}</button>
)}
</div>
Place this outside of render function.

how to map image array with filter in ReactJS

I'm absolutely beginner in ReactJS and I want to filter and map
const Shop = [
{
img : jam,
category: 'jam'
},
{
img : headset,
category:'headset'
},
{
img : sepatu,
category:'sepatu'
}
];
let ShopItem = [
{
id : 1,
img : jam,
category : 'jam',
price : 'Rp. 900,000'
},
{
id : 2,
img : jam,
category : 'jam',
price : 'Rp. 900,000'
},
{
id : 3,
img : headset,
category : 'headset',
price : 'Rp. 900,000'
}
]
function Content(){
const [shopItems,toogleshopItems]=useState(false);
let [items,showItems]=useState('');
const toogleShop = ()=>{
toogleshopItems(!shopItems);
console.log(shopItems);
}
function showshopItems(){
return(
<>
{Shopitem.map((shopitem, shopitemIndex) => {
return Shop.map((shop, shopIndex) => {
if (shop.category == shopitem.category)
return <img className="image-place" src={shopitem.img} />;
});
})}
</>
)
}
return(
<>
<div className="content">
<div className="content-wrapper">
<div className="content-title">Browse Categories</div>
<div className="image-flex">
{
Shop.map((shops)=>
<React.Fragment key={shops.category}>
<img onClick={toogleShop} className="image-place" src={shops.img}/>
</React.Fragment>
)}
</div>
<hr/>
{shopItems? showshopItems() : console.log("no") }
</div>
</div>
</>
)
}
export default Content
how to map shopitem image that has the same category value with shop category value...
when I click picture watch
if I click the picture 1 it will show child picture that has category jam
Finally I found out whats your problem. You should have a state for getting current category when user clicks on the top image and filter the items based on the state. I've created a sample based on your request:
export default function App() {
const [currentCategory, setCategory] = useState();
const TopSlide = () => {
return (
<div className="topSlide">
{Shop.map((shop, index) => {
return (
<img
alt=""
className="topSlideImage"
src={shop.img}
onClick={() => {
setCategory(shop.category);
}}
/>
);
})}
</div>
);
};
const DownSlide = () => {
return (
<div className="downSlide">
{ShopItem.filter((x) => x.category === currentCategory).map(
(shop, index) => {
return <img alt="" className="downSlideImage" src={shop.img} />;
}
)}
</div>
);
};
return (
<div className="App">
{TopSlide()}
<hr />
{DownSlide()}
</div>
);
}

How to make a dynamic modal in React?

I would like to open a modal when I click on a card, and this modal contains more informations about the card content that was clicked.
Here in my example, the cards are replaced by buttons.
So here how to change the modal content depending on the button clicked ?
I saw this post : Change react-modal data dynamically. But I don't understand his solution.
App.js :
function App() {
const [modal, setModal] = useState({ show: false, data: null });
const openNoWebsite = () => {
setModal({ show: true, data: { title: 'No website in sight', subtitle: 'Having a website in 2021 is essential.' } });
};
const openMoreMoney = () => {
setModal({ show: true, data: { title: 'More money', subtitle: "You think you have an awesome product, but people don't really notice it and your sales numbers are not going up."} });
const handleCloseModal = () => {
setModal({ show: false, data: null });
};
return {
<div className="main">
<button className="solidText" onClick={openNoWebsite}>
Button 1
</button>
<button className="solidText" onClick={openMoreMoney}>
Button 2
</button>
{modal.show && modal.data && <Modal closeModal={handleCloseModal} />}
</div>
}
}
Modal.jsx :
function Modal({ closeModal, data }) {
return (
<div className="modal-container">
<div className="modal">
<button className="btn btn--close" onClick={() => closeModal()}>X</button>
<div className="modal__body">
<h1 className="modal__title">{data.title}</h1>
<h2 className="modal__subtitle">{data.subtitle}</h2>
<ul>
<span>How I will help you :</span>
<li>Discover together your goals through a strategy session</li>
<li>Discuss about the possible solutions to implement</li>
<li>A glorious online presence a.k.a. website</li>
</ul>
</div>
<div className="modal__footer">
<p>Wanna put your business out there ?</p>
<button className="btn btn--main">LET'S TALK</button>
</div>
</div>
</div>
)
}
What you can do is to use a state that contain the data you want to show inside your Modal, usually what I do is that I create a state const [modal, setModal] = useState({ show:false, data: null }) and when I press a button I update data accordingly to what I want to show.
Here I used car caracteristic put you can put whatever you want (description, big text, etc...).
The state show is not really usefull but I prefer to use it for clarity (you can just check if !data to know if you want to open or not your modal)
Example
function Modal({ closeModal, data }) {
return (
<div className="modal-container">
<div className="modal">
<button className="btn btn--close" onClick={() => closeModal()}>
X
</button>
<div className="modal__body">
<h1 className="modal__title">
{data.name} {data.color}
</h1>
<h2 className="modal__subtitle">Having a website in 2021 is essential.</h2>
<ul>
<span>How I will help you :</span>
<li>Discover together your goals through a strategy session</li>
<li>Discuss about the possible solutions to implement</li>
<li>A glorious online presence a.k.a. website</li>
</ul>
</div>
<div className="modal__footer">
<p>Wanna put your business out there ?</p>
<button className="btn btn--main">{data.name}</button>
</div>
</div>
</div>
);
}
function App() {
const [modal, setModal] = useState({ show: false, data: null });
const openAudi = () => {
setModal({ show: true, data: { name: 'Audi', color: 'red' } });
};
const openBMW = () => {
setModal({ show: true, data: { name: 'BMW', color: 'blue' } });
};
const handleClose = () => {
setModal({ show: false, data: null });
};
return (
<div className="main">
<button className="solidText" onClick={openAudi}>
Button Audi
</button>
<button className="solidText" onClick={openBMW}>
Button BMW
</button>
{modal.show && modal.data && <Modal closeModal={handleClose} data={modal.data} />}
</div>
);
}
I found this solution, not sure if it's the best but it work.
const [modal, setModal] = useState({ show: false, requestedModalId: 0});
const handleOpenModal = (id) => {
setModal({ show: true, requestedModalId: id });
};
const handleCloseModal = () => {
setModal({ show: false });
};
return {
<div className="main">
<button onClick={() => handleOpenModal(1)}>Button 1</button>
<button onClick={() => handleOpenModal(2)}>Button 2</button>
{modal.show && modal.requestedModalId === 1 && <Modal closeModal={handleCloseModal}>
<div>Modal Child 1</div>
</Modal>}
{modal.show && modal.requestedModalId === 2 && <Modal closeModal={handleCloseModal}>
<div>Modal Child 2</div>
</Modal>}
</div>
}
function Modal(props) {
return (
<div className="modal-container">
<div className="modal">
<button className="btn btn--close" onClick={() => props.closeModal()}>X</button>
{props.children}
</div>
</div>
)
}

Why is my code updating an array after the results are displayed rather than before?

So I have this piece of code in my react app and am trying to use it to update the "siblingarray" state in the app.js file based on a prop that was passed into it (category), but for some reason even when the prop.category is updated the array that posts to my console is the previous category's siblingarray.
CategoryList component that should update the sibling array after the new category prop is passed into it from App.js:
class CategoryList extends Component {
constructor() {
super();
this.state = {
menuClicked: false,
classes: ["list-item"],
listitems: [],
super: 0,
siblingarray: [],
setup: 0
};
}
componentDidUpdate(prevProps, prevState) {
//test to make sure if update is required
if (
(this.state.setup === 0 || this.props.setup === 0) &&
(this.props.category != prevProps.category || this.state.setup === 0) &&
this.props.category !== 0
) {
/*this.setState({
setup:1
})*/
axios
.post("../api/pochecklist.php", {
category: this.props.category
})
.then(res => {
this.setState(
{
listitems: res.data,
setup: 1
},
this.updateSiblingArray()
);
});
}
}
updateSiblingArray = () => {
//console.log("uupdatesublei");
//console.log(this.state.siblingarray);
this.state.listitems.map(item => {
this.setState({
siblingarray: this.state.siblingarray.concat(item.duplicateid)
});
if (Array.isArray(item.sub)) {
{
item.sub.map(subitem =>
this.setState({
siblingarray: this.state.siblingarray.concat(subitem.duplicateid)
})
);
}
}
});
};
clickHandler = (sup, id) => {
if (sup === 0) {
this.setState({
super: id
});
} else {
this.setState({
super: sup
});
}
};
render() {
//console.log(this.state.siblingarray);
return (
<div>
{this.state.listitems.map(item => {
{
if (Array.isArray(item.sub)) {
//console.log(item.sub);
//console.log(this.state.siblingarray);
return (
<div>
<ListItem
siblingarray={this.state.siblingarray}
marked={this.props.marked}
menuAdvPrev={this.props.menuAdvPrev}
activeItem={this.props.activeItem}
menuClick={this.props.menuClick}
categoryCollapse={this.clickHandler}
key={item.id}
id={item.duplicateid}
title={item.title}
link={item.link}
super={item.super}
completed={item.completed}
currentSuper={this.state.super}
category={this.props.category}
text={item.description}
/>
{item.sub.map(subitem => (
<ListItem
siblingarray={this.state.siblingarray}
marked={this.props.marked}
menuAdvPrev={this.props.menuAdvPrev}
activeItem={this.props.activeItem}
menuClick={this.props.menuClick}
categoryCollapse={this.clickHandler}
key={subitem.id}
id={subitem.id}
title={subitem.title}
link={subitem.link}
super={subitem.super}
completed={subitem.completed}
currentSuper={this.state.super}
category={this.props.category}
text={subitem.description}
/>
))}
</div>
);
} else {
return (
<ListItem
siblingarray={this.state.siblingarray}
marked={this.props.marked}
menuAdvPrev={this.props.menuAdvPrev}
activeItem={this.props.activeItem}
menuClick={this.props.menuClick}
categoryCollapse={this.clickHandler}
id={item.duplicateid}
key={item.id}
title={item.title}
link={item.link}
super={item.super}
completed={item.completed}
currentSuper={this.state.super}
category={this.props.category}
text={item.description}
/>
);
}
}
})}
</div>
);
}
}
App.js Code having to do with updating the category:
circleClickHandler = (category, name) => {
this.setState(
{
circleClasses: this.state.circleClasses.concat("hidden"),
listClasses: this.removeClassFromArray(
this.state.listClasses,
"hidden"
),
backArrowClasses: [],
category: category,
categoryName: ": " + name
},
() =>
this.setState({
listClasses: this.state.listClasses.concat("clicked"),
setup: 1
})
);
};
backClickHandler = () => {
this.setState({
menuClicked: 0,
list: [],
listClasses: ["hidden"],
infoClasses: ["hidden"],
quizInfoClasses: ["hidden"],
backArrowClasses: ["hidden"],
category: 0,
categoryName: "",
circleClasses: [],
infoTitle: "",
infoText: "",
infoLink: "",
siblingarray: [],
activeItem: 0,
marked: 0,
setup: 0
});
};
render() {
return (
<div id="App">
<div id="content-wrap">
<header className="header">
<div
id="back-arrow"
onClick={this.backClickHandler}
className={this.state.backArrowClasses.join(" ")}
>
←
</div>
PO Onboarding Checklist{this.state.categoryName}
</header>
<div id="sub-header">
<p>
</p>
</div>
<div
id="circle-container"
className={this.state.circleClasses.join(" ")}
>
<Circle
percentage="80"
category="1"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="60"
category="2"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="95"
category="3"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="45"
category="4"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="12"
category="5"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="20"
category="6"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="100"
category="7"
circleClick={this.circleClickHandler}
/>
<Circle
percentage="26"
category="8"
circleClick={this.circleClickHandler}
/>
</div>
<div
id="po-onboarding-list-container"
className={this.state.listClasses.join(" ")}
>
<LearningObjectives
activeItem={this.props.activeItem}
menuClick={this.props.menuClick}
categoryCollapse={this.clickHandler}
id={0}
title="Learning Objectives"
super={0}
completed={1}
currentSuper={this.state.super}
category={this.props.category}
text={"here are your learning objectives"}
/>
<CategoryList
setup={this.state.setup}
activeItem={this.state.activeItem}
marked={this.state.marked}
menuAdvPrev={this.menuAdvanceAndPreviousHandler}
menuClick={this.menuClickHandler}
category={this.state.category}
/>
<Quiz
activeItem={this.state.activeItem}
quizClick={this.quizClickHandler}
categoryCollapse={this.menuClickHandler}
id={"1"}
completed={1}
currentSuper={this.state.super}
category={this.state.category}
/>
</div>
<div
id="po-onboarding-info-container"
className={this.state.infoClasses}
>
<ListInfo
id={this.state.activeItem}
title={this.state.infoTitle}
info={this.state.infoText}
onClick={this.menuClickHandler}
link={this.state.infoLink}
siblingarray={this.state.siblingarray}
advanceinfo={this.advanceInfo}
previousinfo={this.previousInfo}
/>
</div>
<div
id="po-onboarding-quiz-container"
className={this.state.quizInfoClasses}
>
<QuizInfo
id={this.state.activeItem}
category={this.state.category}
onClick={this.menuClickHandler}
advanceinfo={this.advanceInfo}
previousinfo={this.previousInfo}
/>
</div>
</div>
<div id="footer">
<p>Logical Integrations, Inc.</p>
</div>
</div>
);
}
So basically, as an example, when I click on a circle corresponding to category 1 it should load sibling array:[2,4,5,6,7,8] and it does! But then when I click on the back arrow and select a circle corresponding with category 2, rather than updating the sibling array with the new values, it retains the old ones of [2,4,5,6,7,8]. I have tried to figure out how to fix this for days. Any help would be really appreciated. Thanks!
I actually figured it out guys. Well not so much figured out why the problem was occurring but found a way to get around the problem. I basically had to reinitialize the CategoryList component when I clicked the back arrow in App.js. Now it all works! I f anyone could still explain to me why it wasn't working otherwise I would still really appreciate it though. Thank you.

Categories