why isn't my useEffect navigate not working? - javascript

I am making a sign-up form but my use effect is not working. There are no errors and my mentor says it should work. We are kind of stuck on this bug. Please help. Everything else works including the rejection event. The fetch data works. It shows up as the JSON data.
const SignupPage = () => {
const navigate = useNavigate();
function errormessage(){
return 'something went wrong'
}
//---------------------------------------------
const errorDiv = useRef('')
const [error,errorUpdate] = useState(null)
//-------------------------------------------------
async function FormSubmit(){
const emailInput = document.getElementById('email')
const passwordInput = document.getElementById('password')
const email = emailInput.value
const password = passwordInput.value
const button = document.getElementById('button')
try{
button.addEventListener('click',(event)=>{
event.preventDefault()
})
const Options = {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({email:email,password:password})
}
const fetchUserCreate = await fetch('http://localhost:5000/Signup',Options)
const jsonData = await fetchUserCreate.json()
jsonData.catch(error=>{
return PromiseRejectionEvent
})
useEffect(()=>{
if(jsonData.ok){
return navigate('/')
}
},[jsonData])
//console.log(jsonData)
} catch (error) {
errormessage()
errorUpdate(errormessage())
}
}
return (
<div className="container">
<div className="black"></div>
<video src={SnowVid} type="video/mp4" autoPlay muted loop id='video1'></video>
<div id="login-container">
<h2 className="title-galixy">I0I</h2>
<div id="Signup">
<b><h2 className="title-card">Signup</h2></b>
{/* ---------------------------- */}
<i><h3 className="email">Email</h3></i>
<div className="input"><input name='email' id='email' type="text" className="inputs" /></div>
<i><h3 className="Password">Password</h3></i>
<div className="input"><input name='Password' id='password' type="text" className="inputs" /></div>
<div className="error" ref={errorDiv} >{error}</div>
<div className="button"><button onClick={()=>{FormSubmit()}} className='LastButton' id='button'><b>Login</b></button></div>
</div>
</div>
</div>
);
}
k
useEffect(()=>{
if(jsonData.ok){
return navigate('/')
}
},[jsonData])
I wanted it to navigate to / if jsonData is not an error. It is my signup page.

There are some issues with your code:
useEffect is suppose to be outside of the scope of FormSubmit function. It should be in the scope of the component's render phase. (right before the declaration of FormSubmit function)
You should call the preventDefault function from the event that FormSubmit is getting.
Instead of getting the input values from the global document, get them by extracting the values from the form event.
You can use <form onSubmit={(event) => FormSubmit(event)} />, or with
<button type="submit" onClick={(event) => FormSubmit(event)}/> for getting it.
Side notes:
I would remove jsonData.catch; you already catch the error inside the catch phase.
If you want to navigate to '/' when you are getting the data, you can call navigate('/') immediately when you are getting the required data.
Another option would be to use the useEffect outside of the scope of this function - to lift it up to the wrapper component scope. (SignupPage)
Notice that if you are deciding to lift the useEffect up - you would have to use another state that will trigger the useEffect properly.
const navigate = useNavigate();
const [error, errorUpdate] = useState("");
const [shouldNavigate, setShouldNavigate] = useState(false);
useEffect(() => {
if (shouldNavigate) {
navigate("/");
}
}, [shouldNavigate]);
async function FormSubmit() {
const emailInput = document.getElementById("email");
const passwordInput = document.getElementById("password");
const email = emailInput.value;
const password = passwordInput.value;
try {
const options = {
method: "POST",
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
},
body: JSON.stringify({ email: email, password: password }),
};
const fetchUserCreate = await fetch(
"http://localhost:5000/Signup",
options
);
const jsonData = await fetchUserCreate.json();
if (jsonData.ok) {
setShouldNavigate((prev) => !prev);
}
} catch (error) {
errorUpdate(error?.message);
}
}

Related

Redux -Having a 404 errror and state.category.push(action.payload) not a function error

I'm getting a 400 client error and saying state.category.push is not a function and the current state is pending. I will be happy if someone helps me out on this. The category array is not accepting the data coming into it. I have both the form file and the reducer file down there. I am trying to create a category array to accepts users category and later output it out.
blogSlice.js
My Reducer file
const urlParams = new URLSearchParams(window.location.search)
const TokenAuthless = urlParams.get('enter')
if(TokenAuthless){localStorage.setItem('authless',
JSON.stringify(TokenAuthless))}
var Token = JSON.parse(localStorage.getItem("authless"))
const initialState = {
blogItems: [],
isLoading: null,
category: [{}],
authors: [],
}
const categoryUrl = 'api/v1/admin/createBlogCat';
var api_origin = 'xxxxxxxxxxxx'
export const insertCategory = createAsyncThunk('blog/insertCategory', async(data,
thunkAPI) => {
const{ rejectWithValue } = thunkAPI;
try {
const res = await fetch(`${api_origin}/${categoryUrl}`,{
method :'POST',
mode: 'cors',
body: JSON.stringify({data}),
headers : {
'Authorization': `Bearer ${Token}`,
'Content-type':'application/json',
'Accept':'application/json',
'Access-Control-Allow-Origin':'*',
},
})
const catData = await res.json();
return catData.data;
} catch (error) {
return rejectWithValue(error.message)
}
})
[insertCategory.pending]:(state, action) => {
state.isLoading = true;
},
[insertCategory.fulfilled]: (state, action) => {
state.isLoading = false;
console.log(action.payload);
console.log(current(state))
state.category.push(action.payload);
console.log('the state category', state.category);
},
[insertCategory.rejected]:( state, action ) => {
state.isLoading = false;
},
CreateCategory.js
Creating a form to accept the input here
const CreateCatAut = () => {
const [name, setName] = useState('');
const dispatch = useDispatch()
const handleSubmit=(e)=>{
e.preventDefault();
const categoryData = {name}
dispatch(insertCategory(categoryData));
console.log(categoryData)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Active Input"
value={name}
onChange={(e)=> setName(e.target.value)} />
)
It means that your state.category is not an array. At
state.category.push(action.payload)
I assigned a value to the state.category which is not yet an array. By first creating an array and then putting that element into it.
state.category= [action.payload]
and at the next iterations, i will have an array with one element and can use push on it.

How do I send data from multiple input fields via a single PUT request?

I'm using React, Postgresql, Express, Node
I have a modal with several Input fields and a postgresql table. I want a single button that would take info from all the inputs to add to the table. Atm I have a single input field that works, so I'd need to duplicate them but make sure each field is mapped to a different table column
Current input form only for that adds a entry in scontact_name column, results in a new row where all the other columns are null
//Add entry to contact table route
app.post("/contact", async (req, res) => {
try {
const {scontact_name} = req.body;
const newContactName = await pool.query('INSERT INTO supplier_contacts (scontact_name) VALUES ($1)', [scontact_name]);
res.json(newContactName);
} catch (err) {
console.error(err.message);
}
})
//onSubmit method
const InputContact = () => {
const [scontact_name, setName] = useState([]);
const onSubmitForm = async(e) => {
e.preventDefault();
try {
const body = {scontact_name};
const response = await fetch("http://localhost:5000/contact", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(body)
})
window.location="/";
console.log(e);
} catch (err) {
console.error(err.message)
}
}
return <Fragment>
<form className="d-flex mt-4" onSubmit={onSubmitForm}>
<input
type="text"
className="form-control"
value={scontact_name}
onChange={e => setName(e.target.value)}
/>
<button className="btn btn-success">Add</button>
</form>
<Fragment>
My Solution
The way I made it work is as so:
//Add entry to contact table route - update req.body and sql statement
app.post("/contact", async (req, res) => {
try {
const {scontact_name, **scontact_title**, **...**} = req.body;
const newContactName = await pool.query('INSERT INTO supplier_contacts (scontact_name, **scontact_title**, **...**) VALUES ($1, **$2**, **$3**, **...**)', [scontact_name, **scontact_title**, **...***]);
res.json(newContactName);
} catch (err) {
console.error(err.message);
}
})
//Add methods for useState() to every column
const InputContact = () => {
const [scontact_name, setName] = useState([]);
const [scontact_title, setTitle] = useState([]);
const [scontact_email, setEmail] = useState([]);
const [scontact_phone, setPhone] = useState([]);
const [supplier_id, setSupplierID] = useState([]);
const onSubmitForm2 = async(e) => {
e.preventDefault();
try {
const body = {scontact_name, scontact_title, scontact_email, scontact_phone, supplier_id};
const response = await fetch("http://localhost:5000/contact2", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(body)
})
//window.location="/";
console.log(body);
} catch (err) {
console.error(err.message)
}
}
//Update value parameter to appropriate column
return <Fragment>
<div className="px-5">
<label htmlFor="contactName">Contact Title</label>
<input
type="text"
className="form-control"
value={scontact_title}
onChange={e => setTitle(e.target.value)}
/></div>
</Fragment

Why's my code firing inconsistently using onDoubleClick to send the server data?

I cannot figure out for the life of me why the first pair of onDoubleClick fires 500 server error but on all subsequent onDoubleClicks, it fires 200 correctly but with inconsistent behavior depicted below.
For illustration purposes, this is what's happening:
double click on image A for user ID 1 -> 500 error
double click on image A for user ID 1 -> 200 success (updates successfully for user ID 1)
double click on image B for user ID 2 -> 200 success (updates successfully for user ID 1 when it should've been updated for user ID 2)
and so on....
I've checked all over SO, even tried all of the suggestions on the most popular question asked on this topic on SO but to no avail. I've hit a brick wall.
I believe it has something to do with the asynchronous nature of hooks and how I'm defining them but I'm not entirely sure.
How can I make it so that upon double clicking any image, the corresponding data gets sent to the server correctly on the first pair of double clicks as well as any subsequent pair double clicks (i.e. on a different image)?
For illustration purposes, this how I'd like it to behave:
double click on image A for user ID 1 -> 200 success (updates successfully for user ID 1)
double click* on image b for user ID 2 -> 200 success (updates successfully for user ID 2)
and so on....
const Images = () => {
let authToken = localStorage.getItem("token");
const [uploadsData, setUploadsData] = useState([]);
const [likedPhotoUserId, setLikedPhotoUsedId] = useState("");
const [like, setLike] = useState(0);
useEffect(() => {
getUploads();
}, []);
useEffect(() => {}, [uploadsData]);
const getUploads = () => {
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
axios
.get("http://localhost:8005/api/get-user-uploads-data", { headers })
.then((resp) => {
console.log(resp.data);
setUploadsData(resp.data);
})
.catch((error) => {
console.log(error);
});
};
const handleLikesBasedOnUserId = (e) => {
setLikedPhotoUsedId(e);
sendUserLikePost();
};
const sendUserLikePost = () => {
const url = "http://localhost:8005/api/post-user-like";
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
let data = {
like: like,
UserID: likedPhotoUserId,
};
console.log(data);
axios
.post(url, data, { headers })
.then((resp) => {
console.log(resp.data);
})
.catch((error) => {
console.log(error);
});
};
const displayUploadsData = () => {
return uploadsData.map((photos, index) => {
return (
<ImagesGrid
src={photos.url}
likes={photos.likes}
userName={photos.name}
key={index}
doubleClick={handleLikesBasedOnUserId}
value={photos.UserID}
/>
);
});
};
return <>{displayUploadsData()}</>;
};
const ImagesGrid = (props) => {
const createUserPhotoNodes = () => {
return (
<section className="gallery">
<div className="container">
<form method="POST" name="likes">
<div className="img-container">
<img
src={props.src}
alt="Photo"
className="gallery-img"
onDoubleClick={() => props.doubleClick(props.value)}
/>
<h2 className="userName">{props.userName}</h2>
<h2 className="likes" onChange={props.onChange}>
Likes {props.likes}
</h2>
</div>
</form>
</div>
</section>
);
};
return <>{createUserPhotoNodes()}</>;
};
You have hit the nail on the head – it's because setState is asynchronous and you're seeing stale props or state to boot.
With
const handleLikesBasedOnUserId = (e) => {
setLikedPhotoUsedId(e);
sendUserLikePost();
};
const sendUserLikePost = () => {
// ...
let data = {
'like': like,
'UserID': likedPhotoUserId
};
// ...
likedPhotoUserId will not have updated before you call sendUserLikePost, so likedPhotoUserId is ''. (By the time you call handleLikes... again it will have the "previous" value.)
I don't see a reason why likedPhotoUserId should be a state atom anyway, since all you use it for is sendUserLikePost – just make it a regular ol' parameter:
const handleLikesBasedOnUserId = (e) => {
sendUserLikePost(e);
};
const sendUserLikePost = (likedPhotoUserId) => {
// ...
let data = {
'like': like,
'UserID': likedPhotoUserId
};
// ...

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

How to implement the 'show more' button?

I want to implement button behavior Show more.
When I click the button, new objects must be loaded without rebooting.
I do const [data, setData] = useState(users); to initialize the first 10 users.
When I press the button, that to this array data, I add the following 10 users
var users = response.data;
for (var i=0; i < users.length; i++) {
data.push(users[i]);
}
setData(data);
But nothing is rendered in the browser.
how to do it correct?
const Cards = ({users, bottomUrl }) => {
const [data, setData] = useState(users);
const handleSubmit = e => {
e.preventDefault();
const page = bottomUrl.slice(-1);
const axios = require('axios');
const url = '/users';
axios.get(url, {
params: { page: page }
}, {headers: {'Content-Type': 'application/json'}})
.then(function (response) {
var users = response.data;
for (var i=0; i < users.length; i++) {
data.push(users[i]);
}
setData(data);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
};
return (
<div>
<div className="users-list">
{data.map((element, elIndex) => (
<UserCard
key={elIndex}
lastName={element.lastName}
firstName={element.firstName}
description={element.description}
/>
))}
</div>
<div className="users-page-button-container">
<a className="button" onClick={handleSubmit} href={bottomUrl}>Show more</a>
</div>
</div>
)
};
You are pushing on the state object data. You must use an intermediate variable and pass that to setData
You are manipulating the state directly, which is wrong. Please update the handleSubmit method as below.
const handleSubmit = e => {
e.preventDefault();
const page = bottomUrl.slice(-1);
const axios = require('axios');
const url = '/users';
axios.get(url, {
params: { page: page }
}, {headers: {'Content-Type': 'application/json'}})
.then(function (response) {
users = response.data;
setData([...data, ...users]); // You need to save the previous data along with the new data and pass it together to setData function. So here data = previous data + new users data
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
};

Categories