i have a function called login that redirects the user to the main page if everything was ok. Then, on the main page, i want to fetch some user info with useEffect using the token the was stored when the user logged in, but nothing happens. Only when i refresh the page i get the data.
login function
export const login = ({ email, password, history }) => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:5000/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
password,
}),
});
const data = await response.json();
if (data.status === 200) {
localStorage.setItem("userToken", data.user);
history.push("/");
} else {
dispatch(
setNotification({
variant: "error",
message: data.message,
})
);
}
} catch (e) {
console.log(e.message);
}
};
};
fetch user funtion
export const fetchUser = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:5000/userInfo", {
headers: {
"x-access-token": localStorage.getItem("userToken"),
},
});
const data = await response.json();
dispatch(setUser({
id: data.id,
fullname: data.fullname,
email: data.email
}))
} catch (error) {}
};
};
useEffect on my main page
useEffect(() => {
dispatch(fetchUser());
}, []);
backend function
module.exports.getCurrentUser = async (req, res) => {
const token = req.headers["x-access-token"];
try {
const verifyToken = jwt.verify(token, "123");
const user = await User.findOne({ email: verifyToken.email });
return res.json({
id: user._id,
fullname: user.fullname,
email: user.email
})
} catch (error) {}
};
The 2nd parameter to useEffect tells it when it needs to run. It only runs if one of the values in the array has changed. Since you pass an empty array, none of the values in it have changed.
This is presuming your app probably starts at '/', then detects there is no user so switches to the login screen. When it goes back to the root, it only executes useEffect if something in the array has changed from the previous render.
As it is, the isMounted doesn't make much sense. This could simply be:
useEffect(() => {
dispatch(fetchUser());
});
You're calling setUser, but what is calling your login function?
Related
I keep trying to call an api from my getServerSideProps function in my Next.js application, but everytime I try to do so I get this error
"Error: Error serializing .customerId returned from getServerSideProps in "/pricing".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value."
This is the code for the Pricing page
const Pricing = ({customerId}) => {
console.log(customerId, 'Customerid Pricing')
return(
<div>
<ProductDisplay customerId={customerId}/>
</div>
)
}
export const getServerSideProps = async (ctx) => {
//create customer variable
let customerId
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// get the user session from supabase
const { data: { session }, } = await supabase.auth.getSession()
//get the user email from the session
const email = session.user.email
//create stripe customer
const createCustomer = async () => {
let createdCustomer;
let customerAvailable;
try {
const checkCustomer = await stripe.customers.search({
query: `email: '${email}'`
})
createdCustomer = JSON.parse(JSON.stringify(checkCustomer))
customerAvailable = createdCustomer.data
} catch (error) {
console.log(error)
}
if (customerAvailable.length) {
customerId = createdCustomer.data[0].id
} else {
try {
const res = await fetch('/api/create-customer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email
})
})
const newCustomer = await res.json();
customerId = newCustomer.customer.id
} catch (error) {
console.log(error)
}
}
}
if (session) {
createCustomer()
} else {
return{
redirect: {
destination: '/signin',
permanent: false,
}
}
}
return {
props: {
customerId
},
}
}
export default Pricing;
This is the /api/create-customer the fetch function is trying to call
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
const handler = async (req, res) => {
const {email} = req.body
try {
const customer = await stripe.customers.create({
email: email
});
res.status(200).json({
code: 'customer_created',
customer,
})
} catch (error) {
console.log(error);
res.status(400).json({
code: 'customer_creation_failed',
error: error
})
}
};
export default handler;
please help me, thank you very much.
I have create backend using express and mongodb database. I am trying to fetch data in react but getting an error while fetching the data as show. Please can anyone tell what the solution of above error is and how can i fetch data from the backend
const Register = () => {
const [values, setValues] = useState({
name: "",
age: "",
country: "",
email: "",
});
const setData = (e) => {
console.log(e.target.value);
const { name, value } = e.target;
setValues((val) => {
return {
...val,
[name]: value,
};
});
};
const addData = async (e) => {
e.preventDefault();
const { name, age, country, email } = values;
const res = await fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
age,
country,
email,
}),
});
const data = await res.json();
console.log(data);
if (res.status === 404 || !data) {
console.log("Error");
} else {
console.log("Data added successfully");
}
};
Here below is the backend code where the post function is performed.
router.post("/register", async (req, res) => {
const { name, age, country, email } = req.body;
if (!name || !age || !country || !email) {
res.status(404).send("Some data is missing");
}
try {
const preuser = await Crud.findOne({ email: email });
console.log(preuser);
if (preuser) {
res.status(404).send("The user already exists");
} else {
let addUser = new Crud({
name,
age,
country,
email,
});
addUser = await addUser.save();
res.status(201).json(addUser);
console.log(addUser);
}
} catch (error) {
res.status(404).send(error);
}
});
await fetch leads to an exception when the HTTP status is ≥ 400. You must add a try-catch block to handle such exceptions:
try {
const res = await fetch("/register", {...});
} catch(exception) {
// Handle the exception
}
Also, HTTP status 404 should be used when a resource is not found. You use it when a user already exists (where status 400 would be more appropriate) or in case of a database error (when 500 would be more appropriate).
I'm student in web development. Currently, I'm trying to build a basic project, where I'm stack in implementing reset password feature, I really need help in how fetching reset password API in front-end using Axios. In short, the reset password API that I implemented works fine on Postman, but whenever I tried to pass in front-end and fetch the API in order to enable users to enter their new password and passwordValidation I kinda lost, below I share my code snippets:
backend code reset password
resetPassword = async(req, res) => {
try {
// Step 1: Get user based on the token
const validateHashedToken = crypto
.createHash('sha256')
.update(req.params.token)
.digest('hex');
const user = await User.findOne(
{
passwordResetToken: validateHashedToken,
passwordResetExpires: { $gt: Date.now() }
});
user.password = req.body.password;
user.passwordValidation = req.body.passwordValidation;
user.passwordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save();
// Step 3: Update the "passwordChangedAt" date
// Step 4: Log the user in and send a JWT
genResJWT(user, 200, res);
} catch (error) {
console.log('error', error)
}
};
Routes:
router
.route('/api/v1/users/resetpassword/:token')
.get(viewsController.getResetPasswordUrl)
.patch(viewsController.resetpassword);
controllers
exports.getResetPasswordUrl = async(req, res) => {
try {
const { token } = req.params.token;
const validToken = await User.findOne(
{
passwordResetToken: token
}
);
res.status(200).render('resetPassword',
{
title: 'resetpassword',
token: validToken
});
} catch (error) {
console.log(error);
}
};
exports.resetpassword = (req, res) => {
// I'm stack here and I really need help
res.status(200).render('profile', {
title: 'reset password successfuly'
});
};
front-end fetching api code:
import axios from 'axios';
export const resetPassword = async (password, passwordValidation) => {
try {
const res = await axios({
method: 'PATCH',
url:
`http://127.0.0.1:3000/api/v1/users/resetpassword/:token`,
data: {
password,
passwordValidation
}
});
if (res.data.status === 'success') {
window.setTimeout(() => {
location.replace('/me');
}, 500);
}
} catch (error) {
console.log('error', error.response.data.message);
}
};
On the front end, you are making a request to http://127.0.0.1:3000/api/v1/users/resetpassword/:token. Since token is a route parameter, you are directly passing in the string ":token" and not the actual value of the token.
Try this instead:
const res = await axios({
method: 'PATCH',
url:
`http://127.0.0.1:3000/api/v1/users/resetpassword/${token}`,
data: {
password,
passwordValidation
}
});
where token is a variable you need to define.
Assuming that you are using express, here is some documentation about parameter routing: https://expressjs.com/en/guide/routing.html#route-parameters
I fixed my issue with the following steps:
1- Use only GET request in my '/resetpassword/:token' route and submit the PATCH request with Axios.
2- Pass the 'token' along with the 'password' and the 'passwordValidation' as input data in the PATCH request.
3- create a hidden input within the 'resetPassword' form in order to submit the 'token' with the password and the 'passwordValidation' whenever users confirm their updated password.
Below is my code snippet in order to explain how goes the solution:
Routes:
router.get(
'/resetpassword/:token',
viewsController.resetPassword
)
controllers
exports.resetPassword = (req, res) => {
const token = req.params.token;
res.status(200).render('/login', {
title: 'reset password successfuly', { token }
});
};
front-end fetching API code:
import axios from 'axios';
export const resetPassword = async (password, passwordValidation, token) => {
try {
const res = await axios({
method: 'PATCH',
url:
`/api/v1/users/resetpassword/${token}`,
data: {
password,
passwordValidation
}
});
if (res.data.status === 'success') {
window.setTimeout(() => {
location.assign('/login');
}, 1000);
}
} catch (error) {
console.log('error', error.response.data.message);
}
};
the resetPassword form:
extends goaheadtravel
block content
main.main
.resetpassword-form
h2.heading-secondary.ma-bt-lg Please enter a new password and validate it
form.form.resetpassword--form
.form__group.ma-bt-md
label.form__label(for='password') Password
input#password.form__input(type='password' placeholder='••••••••' required='' minlength='8')
.form__group.ma-bt-md
label.form__label(for='passwordValidation') Confirm password
input#passwordValidation.form__input(type='password' placeholder='••••••••' required='' minlength='8')
input#resetToken(type='hidden' value=`${token}`)
.form__group.right
button.btn.btn--green Confirm new password
Hope that my solution will help other developers!
I am working on an Application which i have also deployed in heroku. The issue is that when I login in using heroku, user is nested inside a data object. but when I work locally or use postman, user isnt nested.
Help Please.
I get this response on the deployed version.
data: {
user: {
email: "my_email"
name: "my_name"
role: "user"
_id: "6205807deeadcfa734f954f3".
}
status: "success"
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyMDU4MDdkZWVhZGNmYTczNGY5NTRmMyIsImlhdCI6MTY0NDg0NTYyMCwiZXhwIjoxNjQ1NDUwNDIwfQ.YeWFNrN8rsLPJvvU8JQDwBVG4aBqqEuo7ssgLrR3O8M"
But when I log in locally, I get the response as
user: {
email: "my_email"
name: "my_name"
role: "user"
_id: "6205807deeadcfa734f954f3".
}
status: "success"
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyMDU4MDdkZWVhZGNmYTczNGY5NTRmMyIsImlhdCI
For Heroku, the USER is nested inside data but for local host and postman, the user isnt nested.
My codes are:
exports.login = catchAsync(async (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return next(new AppError('Please provide email and password!', 400))
}
const user = await User.findOne({ email }).select('+password')
if (!user || !(await user.comparePassword(password, user.password))) {
return next(new AppError('Incorrect email or password', 401))
}
createSendToken(user, 200, req, res)
})
These are my api codes
const createSendToken = (user, statusCode, req, res) => {
const token = signToken(user._id)
res.cookie('jwt', token, {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000
),
httpOnly: true,
})
user.password = undefined
res.status(statusCode).json({
status: 'success',
token,
user,
})
}
For my react, The function code is:
function request(path, { data = null, token = null, method = 'GET' }) {
return (
fetch(`${process.env.REACT_APP_API}${path}`, {
method,
headers: {
Authorization: token ? `Bearer ${token}` : '',
'Content-Type': 'application/json',
},
body:
method !== 'GET' && method !== 'DELETE' ? JSON.stringify(data) : null,
})
.then((response) => {
// If Successful
if (response.ok) {
if (method === 'DELETE') {
// If delete, nothing returned
return true
}
return response.json()
}
// If errors
return response
.json()
.then((json) => {
// Handle Json Error response from server
if (response.status === 400) {
const errors = Object.keys(json).map(
(k) => `${json[k].join(' ')}`
)
throw new Error(errors.join(' '))
}
throw new Error(JSON.stringify(json))
})
.catch((e) => {
if (e.name === 'SyntaxError') {
throw new Error(response.statusText)
}
throw new Error(e)
})
})
.catch((e) => {
// Handle all errors
toast(e.message, { type: 'error' })
})
)
}
The main sign in function
export function signIn(email, password) {
return request('/api/v1/auth/login', {
data: { email, password },
method: 'POST',
})
}
Then I import this into my auth context and execute it there
import {signIn as signInApi} from '../apis'
const AuthContext = createContext()
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(localStorage.getItem('token'))
const [user, setUser] = useState(
JSON.parse(localStorage.getItem('user'))
)
const [loading, setLoading] = useState(false)
const signIn = async (email, password, callback) => {
setLoading(true)
const res = await signInApi(email, password)
if (res.token) {
localStorage.setItem('token', res.token)
localStorage.setItem('user', JSON.stringify(res.user)) // This stores the user in localhost but returns undefined for user in the one deployed to heroku. I have to use
localStorage.setItem('user', JSON.stringify(res.data.user)) which now works on the deployed one but not on the local one
setToken(res.token)
setUser(res.user)
callback()
}
setLoading(false)
}
}
it seems the deployed version is using built in implementaion of createSendToken and not the one you provided. need to check your project structure.
in order to validate this change the function name and the call createSendToken to something else and you will find the issue
I am facing to asynchronism problem :
I create a user in firebase, generating a unique ID for it.
I get this unique ID.
I call an async function to persist this ID with AsyncStorage method.
Problem : The asyncStorage method is called before I get back the generated ID from my user creation. How to deal with this ?
This is my code :
class Subscription extends Component {
constructor() {
super();
this.state = {
email: '',
password: ''
}
}
persistUserId = (userID) => {
try {
AsyncStorage.setItem('userId', userID); // Here, user ID is undefined
} catch (error) {
console.log(error.message);
}
};
updateInputValue = (value, prop) => {
const state = this.state;
state[prop] = value;
this.setState(state);
}
registerUser = () => {
var generatedUserId = '';
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password) // Authentication
.then((res) => {
var user = { // Set Javascript Object to insert
email: this.state.email
}
database.collection("users").add({ // Create the new user generating an ID
'email': user.email,
}).then(function(docRef) {
generatedUserId = docRef.id; // Get the generated ID (The one to persist witch asyncstorage)
}).then(function() {
this.persistUserId(generatedUserId) // Call the AsyncStorage to persist the ID
})
this.props.navigation.navigate('AppPage') // Go to next page.
})
.catch(error => {
alert(error.message)
})
}
For persisting data. According to react-native doc. You need to use async await keyword:
_storeData = async () => {
try {
await AsyncStorage.setItem(
'#MySuperStore:key',
'I like to save it.'
);
} catch (error) {
// Error saving data
}
}
for your case:
persistUserId = async (userID) => {
try {
await AsyncStorage.setItem('userId', userID); // Here, user ID is undefined
} catch (error) {
console.log(error.message);
}
};
Note: Persisting data is async process. That's why you need to use async await
You need to update your firebase then catch as well. Either use bind or use arrow function. Here is updated version:
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password) // Authentication
.then((res) => {
var user = {
// Set Javascript Object to insert
email: this.state.email,
};
database
.collection("users")
.add({
// Create the new user generating an ID
email: user.email,
})
.then( (docRef) => {
generatedUserId = docRef.id; // Get the generated ID (The one to persist witch asyncstorage)
})
.then( () => {
this.persistUserId(generatedUserId); // Call the AsyncStorage to persist the ID
});
this.props.navigation.navigate("AppPage"); // Go to next page.
})
.catch((error) => {
alert(error.message);
});