How can I toggle the button text based on HTTP status? - javascript

Onclick, I'd like to toggle the button text based on the HTTP status. If the status is 200, then forgotPasswordBtnTxt should change to Sent!, else forgotPasswordBtnTxt should change to Something went wrong!.
I know hooks are async but for some reason everything I've tried has failed. What am I doing wrong?
const [forgotPasswordBtnFlag, setForgotPasswordBtnFlag] = useState(false);
const [forgotPasswordBtnTxt, setForgotPasswordBtnTxt] = useState("Send Forgot Password Link");
const [forgotPasswordStatus, setForgotPasswordStatus] = useState(null);
const forgotPassWordClicked = () => {
setForgotPasswordBtnFlag(true);
setForgotPasswordBtnTxt(forgotPasswordStatus === 200 ? "Sent!" : "Something went wrong!");
};
const handleForgotPassword = (e) => {
e.preventDefault();
let dataForgotPassword = {
'email': forgotPasswordEmail
};
axios.post('http://127.0.0.1:8000/api/forgot-password', dataForgotPassword)
.then(resp => {
let okStatus = resp.status;
setForgotPasswordStatus(okStatus);
}).catch(error => {
let failedStatus = error.response.status;
setForgotPasswordStatus(failedStatus);
});
};
<button type="submit" className={`${!forgotPasswordBtnFlag ? 'btn' : 'forgotPasswordTextSuccess'} sendPasswordResetLinkBtn`} onClick={forgotPassWordClicked}>
{forgotPasswordBtnTxt}
</button>

Related

Setting my value to usestate after clicking second time first gets assigned there

It is so that I get a problem like doing that when I write email for the first time and it is sent in the database. Then I expect something to come back. Which also do. But it adds nothing to my usestate at all. But when I try to click again and write email again. Then the message I expected is added to my usestate.
So after the second time I have clicked it works exactly as it should and I want them to but it does nothing the first time.
Here you can see my html part for the react.tsx file.
<form onSubmit={(e) => submitClick(e)}>
<label className='labelValue'>Email</label>
<input
type="text"
className='w-full border-2 border-slate-100 p-2 md:rounded-lg mt-1 md:mt-2'
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder='Email here' />
<button disabled={disabledNow} className='btn w-full'>
{disabledNow ? "Account are createde now." : "Create user"}
</button>
</form>
Here can u see my React for at post to database in my react.tsx file.
const [email, setEmail] = useState('');
const [dataValue, setDataValue] = useState([]);
const [item, setItem] = useState(0);
const [disabledNow, setDisabledNow] = useState(false);
const submitClick = (e) => {
e.preventDefault();
setItem(1);
setDisabledNow(true)
}
useEffect(() => {
const dataMail = {
Email: email
};
let ignore = false;
if (item === 1) {
if (!ignore) {
const fetchData = async () => {
const result = await axios.post("https://localhost:7176/api/user", dataMail)
const fetchItem = await result.data;
setDataValue(fetchItem);//not adding fetchItem first time i make a click onSubmit. But after this i get a fetchItem to setDataValue.
console.log(dataValue);
}
fetchData();
setEmail('');
setItem(0)
ignore = true
if (dataValue.length > 0) {
console.log("Out of Fetch now. HERE")
toast(dataValue[1], { duration: 7000 })
console.log("Notify now HERE")
}
}
}
}, [dataValue, email, item])
As I said, I have also tried to remove async and await and it still gives the same problem that I don't have it on.
I have chained promised instead of async await in useEffect. Also refer to comments in code for better understanding.
// create ignore as global variable.
let ignore = false;
useEffect(() => {
const dataMail = {
Email: email
};
if (item === 1) {
if (!ignore) {
axios.post("https://localhost:7176/api/user", dataMail)
.then((response) => {
const fetchItem = response.data;
setDataValue(fetchItem);
// new data won't be available here, so you need to use fetchItem
console.log(fetchItem);
setEmail('');
setItem(0)
ignore = true
if (fetchItem.length > 0) {
console.log("Out of Fetch now. HERE")
toast(fetchItem[1], { duration: 7000 })
console.log("Notify now HERE")
}
})
.catch((error) => {
console.log(error)
})
}
}
}, [dataValue, email, item])

Fetch request stuck on pending and then doesn't go through

I'm making a full stack Rick and Morty application. Characters on the screen and the user can login and click on them to add them to favorites and then click on them on the favorites page to delete them from the favorites page.
The application works but crashes after a few minutes saying that a fetch request didn't work. In network section of the developer tools, these requests to add or delete characters are coming up as (pending) and then coming up as failures like two minutes later. At the same time, the requests are working from the perspective of the application, meaning that if I add or delete characters as a user and then logout and log back in, the changes are still there. The register and login requests to the backend are working normally with statuses of 200 as well. What's happening here?
The backend:
const express = require('express');
const application = express();
const mongoose = require('mongoose');
application.use(express.json());
mongoose.connect('process.env.DATABASE_PASSWORD')
.then(console.log('Connected to database'));
const db = mongoose.connection;
const port = process.env.PORT || 8080;
application.post('/register', (request, response) => {
const username = request.body.username;
const password = request.body.password;
const favorites = [];
db.collection('data').insertOne({
username,
password,
favorites,
});
});
application.post('/login', async (request, response) => {
const username = request.body.username;
const password = request.body.password;
const findUser = await db.collection('data').findOne({
username,
password,
});
if (findUser) {
response.send({ message: 'Welcome, ' + username + "!", user: username, favorites: findUser.favorites });
} else {
response.send({ message: 'Login unsuccessful'});
}
});
application.post('/addFavorite', (request, response) => {
const userNow = request.body.username;
const favoritesHere = request.body.favoritesCopy;
console.log({userNow, favoritesHere});
db.collection('data').updateOne(
{ username: userNow },
{ $set: { favorites: favoritesHere }},
)
});
application.post('/deleteFavorite', (request, response) => {
const userNow = request.body.username;
const favoritesHere = request.body.theData;
db.collection('data').updateOne(
{ username: userNow },
{ $set: { favorites: favoritesHere }},
);
});
application.listen(port, () => {
console.log('Application listening');
});
The frontend fetch add request (the delete request is similar):
import React, { useState, useEffect } from 'react';
import logo from '../rickandmortylogo.png';
import { useSelector, useDispatch } from 'react-redux';
import { addFavorite } from '../index.js';
const Body = () => {
const [characters, setCharacters] = useState([]);
const [currentName, setCurrentName] = useState('Placeholder');
const [nameInput, setNameInput] = useState('');
const [locationInput, setLocationInput] = useState('');
const [loading, setLoading] = useState(true);
const favorites = useSelector(state => state.favoritesList);
const userNow = useSelector(state => state.currentUser);
const loggedIn = useSelector(state => state.loggedIn);
const dispatch = useDispatch();
useEffect(() => {
let isMounted = true;
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
const getData = async () => {
for (let i = 1; i < 4; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
if (isMounted) {
setCharacters(array);
setLoading(false);
}}
getData();
return () => {
isMounted = false;
}
}, []);
const readInput = (e) => {
setNameInput(e.target.value);
}
const readLocationInput = (e) => {
setLocationInput(e.target.value);
}
const addData = (a, b, c, d) => {
const array = [a, b, c, d];
const favoritesCopy = [...favorites];
favoritesCopy.push(array);
dispatch(addFavorite(array));
if (loggedIn === true) {
fetch('/addFavorite', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
favoritesCopy,
username: userNow,
}),
});
}
};
return (
<div className="pt-5">
<div className="text-center mt-5">
<img src={logo} className="img-fluid" />
</div>
<h2>Click on a character here to add them to your favorites. Choose "Check Favorites" in the menu bar to see your favorites and "Search Characters" to come back.</h2>
<div className="all">
<h4>Search by name:</h4>
<input onChange={readInput} />
<h4>Search by location:</h4>
<input onChange={readLocationInput} />
<br />
<div className="row m-1">
{loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : characters.filter((item) => {
if (nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square" onClick={() => addData(item.name, item.image, item.location.name, item.status)}>
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
)
})}
</div>
</div>
</div>
);
};
export default Body;
You never end the request. You don't send anything in the response and don't call response.end either, nor next. That's why your request never ends.
Here are some examples:
Success message with content
res.status(200).json({ success: true});
Success message without content
res.sendStatus(204);
Of course requests are pending, you never send anything on related actions:
Use res.send and send something, or at least in case of success, send a success status like:
204 for a no content success operation, like a DELETE for example.
201 for a POST operation creating a new resource.
5xx for errors

axios get request not fetch data on first click

I am trying to fetch data on button click. The problem is when I click on the button first-time data is not fetching. but when I click second time data is fetching properly.
here is my code
const learnMores = document.querySelectorAll('.learnMoreId');
const readmoreImg = document.querySelector('#readmoreImg');
const readMoreTitle = document.querySelector('#readMoreTitle');
const readMoresubTitle = document.querySelector('#readMoresubTitle');
const readmoreBody = document.querySelector('#readmoreBody');
learnMores.forEach((learnMore) => {
learnMore.addEventListener('click', (e) => {
e.preventDefault();
if (e.currentTarget) {
const { reamoreid } = e.target.dataset;
axios.get(`/single-readmore/${reamoreid}`).then((response) => {
readmoreImg.src = `/upload/readmore/${response.data.readMoreImage}`;
readMoreTitle.innerText = response.data.title;
readMoresubTitle.innerText = response.data.subTitle;
readmoreBody.innerHTML = response.data.desciption;
}).catch((error)=>{
console.log(error);
})
}
});
});
try using async/await. Also why are you using e.preventDefault()?
learnMore.addEventListener('click', async (e) => {
e.preventDefault();
if (e.currentTarget) {
const { readmoreid } = e.target.dataset;
await axios.get(`/single-readmore/${readmoreid}`).then((response) => {
readmoreImg.src = `/upload/readmore/${response.data.readMoreImage}`;
readMoreTitle.innerText = response.data.title;
readMoresubTitle.innerText = response.data.subTitle;
readmoreBody.innerHTML = response.data.desciption;
}).catch((error) => {
console.log(error);
})
}
});
I don't see any erros in your code, but make sure "e.target.dataset" has values and api not returning any errors

How do I get my data to display on flash cards using JavaScript?

Right now I'm working on full stack application that uses JS on the front and back end. This app lets a user generate their own set of flash cards. Whenever the user clicks "View Cards" data will then be fetched and will display the question and answer on each side of the card. It's supposed to display one card at a time and allows the user to scroll through other cards using either the "Previous" or "Next" buttons. I'm able to successfully fetch data convert it to JSON and can get at least one item from the data base display properly. The problem is whenever I try to scroll through the cards. I'm only able to scroll through some of the cards before the browser returns an error. I've even noticed that some of the cards won't render both sides properly. How could I address these issues?
const flashCard = document.querySelector(".flashcard");
const flipBtn = document.querySelector(".flip-btn");
const prevBtn = document.querySelector(".prev-btn");
const nextBtn = document.querySelector(".next-btn");
let frontOfCard = document.querySelector(".front-content");
let backOfCard = document.querySelector(".back-content");
const displayCards = () => {
getCardInfo()
flipBtn.innerHTML = "Flip"
flipBtn.removeEventListener("click", displayCards)
}
flipBtn.addEventListener("click", displayCards)
const flash = () => {
if (flashCard.style.transform != "rotateX(180deg)") {
flashCard.style.transform = "rotateX(180deg)"
} else {
flashCard.style.transform = "none"
}
}
const getCardInfo = async () => {
const itemBody = {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
}
const data = await fetch(window.location.href, itemBody)
const jsonData = await data.json()
console.log(jsonData)
let idx = 0;
frontOfCard.innerHTML = jsonData[idx].Answer
backOfCard.innerHTML = jsonData[idx].Question
flashCard.style.display = "block";
flipBtn.addEventListener("click", flash);
scrollThroughCards(idx, jsonData);
}
function scrollThroughCards(idx, data) {
prevBtn.addEventListener("click", (e) => {
flashCard.style.display = "none"
setTimeout(() => {
frontOfCard.innerHTML = data[idx--].Answer
backOfCard.innerHTML = data[idx--].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
nextBtn.addEventListener("click", (e) => {
flashCard.style.display = "none"
setTimeout(() => {
frontOfCard.innerHTML = data[idx++].Answer
backOfCard.innerHTML = data[idx++].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
}
app.get("/card/:id", checkAuthenticated, async (req,res) => {
const { id } = req.params
const data = await Card.findAll({ where: { NoteId: id } });
res.render("cards-page", {
noteid: id,
Cards: data
})
});
app.put("/card/:id", checkAuthenticated, async (req,res) => {
const { id } = req.params
const data = await Card.findAll({ where: { NoteId: id } });
res.json(data)
})
app.post("/card/:id", checkAuthenticated, async (req, res) => {
const { Question, Answer, NoteId } = req.body;
const newCard = await Card.create({
Question,
Answer,
NoteId
});
res.redirect(`/card/${NoteId}`)
});
In the scrollThroughCards function, boundary checks were not performed and the increment and decrement operators were misused.
function scrollThroughCards(idx, data) {
prevBtn.addEventListener("click", (e) => {
// there's no more card on the left of index 0
// so exit the function early
if (idx <= 0) return;
flashCard.style.display = "none"
setTimeout(() => {
idx--; // decrease the index first
// then use the modified index
frontOfCard.innerHTML = data[idx].Answer
backOfCard.innerHTML = data[idx].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
nextBtn.addEventListener("click", (e) => {
// there's no more cards beyond the end of the list
// so exit the function early
if (idx >= data.length - 1) return;
flashCard.style.display = "none"
setTimeout(() => {
idx++; // increase the index first
// then use the modified index next
frontOfCard.innerHTML = data[idx].Answer
backOfCard.innerHTML = data[idx].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
}

Correct way to pass async eerror

I have a function which uses Firebase auth to update a user's email:
export const updateEmail = async (email) => {
const user = auth.currentUser;
return user.updateEmail(email);
};
It is used in a function which gets an email from a form (in React) and tries to update the email. If there is an error, we change the state to reflect that.
handleSave = (e) => {
const email = e.target.email.value;
updateEmail(email).catch((err) => {
this.setState({ didError: true, emailError: err.message });
});
};
However, when an error occurs, in the console I get:
My question is: why does this still say 'Uncaught'? Does the .catch() in handleSave not take care of that?
update
Link to relevant Firebase docs
Assuming updateEmail returns a prmise, I guess you can try:
export const updateEmail = (email) => { // no need for async here
const user = auth.currentUser;
return user.updateEmail(email);
};
handleSave = async (e) => {
const email = e.target.email.value;
try{
await updateEmail(email);
}catch(err){
this.setState({ didError: true, emailError: err.message });
}
};
I'm not quite sure since I don't know so much about Firebase, let me suggest something.
export const updateEmail = async (email) => {
const user = auth.currentUser;
const response = await user.updateEmail(email);
if ( response.error ) {
throw new Error( response.error );
}
return "something else";
};

Categories