Issue about posting form data by using axios - reactjs

I'm working on registration. everything was good before adding the profile image.
this is the registration page onSubmit method
const handleSubmit = (event) => {
event.preventDefault();
const user_data = {
userID: userID,
password: password,
firstName: firstName,
lastName: lastName,
email: email
}
dispatch(register(user_data, profileImageFile))
};
and this is the actual axios
const register = async (user_data, imgFile) => {
console.log(user_data, imgFile)
formData.append('userID', user_data.userID);
formData.append('password', user_data.password);
formData.append('firstName', user_data.firstName);
formData.append('lastName', user_data.lastName);
formData.append('email', user_data.email);
formData.append('imgFile', imgFile);
for (var pair of formData.entries()) { console.log(pair[0] + ', ' + pair[1]); }
return await axios.post("/registration", formData
, {
headers: { 'Content-Type': 'multipart/form-data' }
}
)
}
I verified formdata had all the text data and image well. but at the backend, any data wasn't appear.
When I put in another data without an image file, it worked well.
what is the problem with that?

Related

When sign in with google firebase always register user as new

Tech: Firebase, Next.js, Google Sign in, Firebase Stripe exstension
Bug reproduction:
When login with Google
Subscribe on stripe
Stripe saves subscription data for that user in firestore
Logout
Login in with Google and old data are overide with new one, and Subscription is lost
Does anyone had similar problem?
Maybe my implementation of Sign-in is bad, here is the Google Sign in code:
const handleGoogleLogin = () => {
signInWithPopup(auth, googleProvider)
.then(async result => {
if (!result.user) return;
const { displayName, email, uid, providerData, photoURL, phoneNumber } =
result.user;
const name = splitName(displayName as string);
const providerId =
(providerData.length && providerData[0]?.providerId) || '';
const data = {
firstName: name?.firstName || '',
lastName: name?.lastName || '',
email,
photoURL,
phoneNumber,
providerId,
};
await updateUser(uid, data);
})
.catch(error => {
console.log('Google login error: ', error);
});
};
Update user function:
export const updateUser = async (uid: string, data: UpdateUserParams) => {
try {
if (!uid) {
return;
}
await setDoc(doc(firestore, 'users', uid), {
account: {
...data,
initials: `${data.firstName[0]}${data.lastName[0]}`,
},
});
} catch (error) {
console.error('Error updating user: ', error);
}
};
setDoc is overwriting the contents of the document with each sign-in. You should instead use set with merge to prevent overwriting the fields you don't want to lose, or check first if the document exists before creating it.
See also:
https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
Difference Between Firestore Set with {merge: true} and Update

How to persist data using Apollo?

I have a Apollo client and server with a React app in which users can log in. This is the Apollo server mutation for the login:
loginUser: async (root, args) => {
const theUser = await prisma.user.findUnique({
where: {email: String(args.email)},
});
if (!theUser) throw new Error('Unable to Login');
const isMatch = bcrypt.compareSync(args.password, theUser.password);
if (!isMatch) throw new Error('Unable to Login');
return {token: jwt.sign(theUser, 'supersecret'), currentUser: theUser};
},
This returns a JWT and the user that's logging in.
In my React app I have a login component:
// Login.tsx
const [loginUserRes] = useMutation(resolvers.mutations.LoginUser);
const handleSubmit = async (e) => {
e.preventDefault();
const {data} = await loginUserRes({variables: {
email: formData.email,
password: formData.password,
}});
if (data) {
currentUserVar({
email: data.loginUser.currentUser.email,
id: data.loginUser.currentUser.id,
loggedIn: true,
});
window.localStorage.setItem('token', data.loginUser.token);
}
};
This function passes the form data to the LoginUser mutation which returns data if authentication is successful. Then I have a reactive variable called currentUserVar I store the email and id of the user in there so I can use it throughout the application. Finally I store the JWT in a LocalStorage so I can send it for authorization:
// index.tsx
const authLink = setContext((_, {headers}) => {
const token = localStorage.getItem('token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
Everything is working, except for the fact that if a user refreshes the user data is gone and they have to log in again, which is of course quite annoying.
So I was hoping to get some advice on how to persist the data, perhaps using Apollo? I suppose I could add a checkbox with a remember me function that stores the email and id in the LocalStorage and when the app initiates check if there's user data in the LocalStorage and than use that, but I was wondering if there's a better/other way to do this.
When it comes to the login problem , you have set the headers on your every single request , but did you pass a fuction to the ApolloServer constructor that checks the headers from every single request ? Something like this:
const server=new ApolloServer({
typeDefs,
resolvers,
context:async({req})=>{
const me=getMe(req)
return {
models,
me,
process.env.SECRET
}
}
})
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, process.env.SECRET);
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
As for the data persistence part of the question , you have to use setItem to persist the token in the locatStorage.

How can I properly handle request errors with React front end?

Currently I have my backend set up as such on the '/register' route:
registerRouter.post('/', async (req, res) => {
// Validate submitted registration form
const { error } = registerValidation(req.body)
if(error) {
return res.status(400).send(error.details[0].message)
}
try {
// Check if email exists already
const user = await User.findOne({ email: req.body.email })
if(user) {
return res.status(400).send('Email already exists')
}
// If not, begin registering user by hashing the password
const hashedPassword = await bcrypt.hash(req.body.password, 10)
const newUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPassword
})
const savedUser = await newUser.save()
res.send(savedUser)
} catch(error) {
res.sendStatus(500)
}
})
Using Postman I get the proper responses when I make correct/incorrect requests. But when I make requests on my frontend, if it is an incorrect request, e.g. not long enough password, missing a required field, then I just get a 400 response. How can I use the error response to, for example, display the error on-screen for the user to see?
This is my current onSubmit function for the form:
const register = async event => {
event.preventDefault()
axios
.post('/register', newUser)
.then(res => console.log(res))
.catch(err => console.log(err))
}
try to use:
axios
.post('/register', newUser)
.catch(function (error) {
console.log(error.toJSON()); // or maybe exist .toText()
});
(https://github.com/axios/axios#handling-errors)
also convert it on server side:
return res.status(400).send('Email already exists')
to
return res.status(400).send({ error: 'Email already exists' });

Password value magically disappears on form submit

I am trying to set up frontend authentication for a react application running on a Django REST-API.
The registration API endpoint works well if I send the request on Postman. However, the program is behaving very odd when I type information into my frontend registration form and click on submit.
Please look at the code below - I use a useInputState hook to keep track of the state of a given input. This works fine.
The registerUser function takes in username, password and email and sends a Post-Request to the dedicated API Endpoint.
The handleSubmit function checks if the passwords match and throws an error if they don't this works fine as well. If the passwords do match, it calls registerUser and provides the users input.
Now somewhere along the way, the password that has been passed into registerUser disappears (see comments). I have no idea where this issue could come from and any help is greatly appreciated!
function Register() {
const { auth, dispatchAuth } = useContext(AuthContext);
const { dispatchErrors } = useContext(ErrorContext);
const { dispatchMessages } = useContext(MessageContext);
const [username, setUsername] = useInputState("");
const [email, setEmail] = useInputState("");
const [password1, setPassword1] = useInputState("");
const [password2, setPassword2] = useInputState("");
const registerUser = ({ username, password, email }) => {
// headers
const config = {
headers: {
"Content-Type": "application/json",
},
};
// Request body
const body = JSON.stringify({
username,
password,
email,
});
console.log("body:");
console.log(body);
// Output looks like: {"username":"testuser","email":"email#test.com"}
// Where did the password go?!
axios
.post("/api/auth/register/", body, config)
.then((res) => {
dispatchAuth({ type: REGISTER_SUCCESS, payload: res.data });
})
.catch((err) => {
dispatchErrors(returnErrors(err.response.data, err.response.status));
dispatchAuth({ type: REGISTER_FAIL });
});
};
const handleSubmit = (e) => {
e.preventDefault();
if (password1 !== password2) {
dispatchMessages(
createMessage({ passwordsNotMatch: "Passwords do not match" })
);
} else {
console.log(password1); // Console output still contains the password
const newUser = { username, password1, email };
registerUser(newUser);
}
};
EDIT: Okay I'm an idiot and didn't notice that I need to change const newUser = { username, password1, email }; to be const newUser = { username, password, email };
Okay I'm an idiot and didn't notice that I need to change const newUser = { username, password1, email }; to be const newUser = { username, password, email };

Setting state after callback with json data

I'm receiving a callback from axios after a post request. The callback contains data that I need to use. I want add the callback json data my existing state - but after googling I learnt that callbacks + usestate hook doesn't work together well.
const [party, setParty] = useState("")
const SubmitParty = (e) => {
e.preventDefault()
const PartyData = {
party: party,
firstname: firstname,
lastname: lastname,
email: email,
number: number
}
axios.post(
'/api/new/party',
{
party: party,
firstname: firstname,
lastname: lastname,
email: email,
number: number
},
{
params: {
"secret_token": logged.token
}
}
).then(res => {
var user = res.data.data;
console.log(user); //I can see the response here and the array I want.
setParty(user);
console.log(party); // returns just a blank line. Expected to be same as user
}).catch(err => {
console.log(err)
alert("Oops something went wrong. If this problem continues, please contact support asap!")
});
}
I tried using a useeffect afterwards, but it's a catch 22, cause I need the state to deal with with in in useeffect accurately.
If you use useEffect like this your component will send the API request and update party when it resolves.
const [party, setParty] = useState("");
useEffect(e => {
e.preventDefault();
const PartyData = {
party: party,
firstname: firstname,
lastname: lastname,
email: email,
number: number
};
axios
.post(
"/api/new/party",
{
party: party,
firstname: firstname,
lastname: lastname,
email: email,
number: number
},
{
params: {
secret_token: logged.token
}
}
)
.then(res => {
var user = res.data.data;
console.log(user); //I can see the response here and the array I want.
setParty(user);
console.log(party); // returns just a blank line. Expected to be same as user
})
.catch(err => {
console.log(err);
alert(
"Oops something went wrong. If this problem continues, please contact support asap!"
);
});
});
By default this will run on every render. If you want the effect to run only once then you should set useEffect's 2nd parameter to be an empty array, or if you only want it to run when a specific few items change then you can put those in the array.

Resources