Issue
I am trying to create a user profile from calling on axios, using reactjs. But I can only see an updated state when I have useEffect but nowhere else. In loginPage.js, I am trying to get the most updated states of entryfirstName, entryLastName, and role to post to the login function, which ties back into my userProvider.js. I am not sure how else to update the states so any feedback is better than what I got.
I am very new to react and I haven't been able to solve this so any help would be much appreciated!
Here is my code for loginPage.js
export default function SignIn() {
const { login } = useContext(UserContext);
const [email_input, setEmail] = useState('');
const [password_input, setPassword] = useState('');
const [entryfirstName, setFirstName] = useState('');
const [entrylastName, setlastName] = useState('');
const [role, setRole] = useState('');
const [entryuser, setEntryUser] = useState({});
const baseURL = 'http://samplesite.org/';
const handleEmailInputChange = event => {
setEmail(event.target.value);
};
const handlePasswordInputChange = event => {
setPassword(event.target.value);
};
const gatherData = async () => {
try {
const userlogins = await axios.get(`${baseURL}/user`);
setEntryUser(userlogins.data.users);
}
catch (error) {
console.log(error);
}
};
const updateData = (loggedin_id, userFirst, userLast) => {
setRole(loggedin_id);
setFirstName(userFirst);
setlastName(userLast);
}, [role, entryfirstName, entrylastName]);
const submit = () => {
if (email_input === null || password_input === null || email_input === "" || password_input === "") {
alert("Enter username and password");
}
else {
if (entryuser.map(x => x.email === email_input && x.password === password_input)) {
console.log("Email and Password exists");
const loggedin = entryuser.filter(obj => obj.email === email_input);
const loggedin_role = loggedin.map(({ role }) => `${role}`);
const loggedin_id = loggedin_role.toString();
const userNames_1 = loggedin.map(({ firstName }) => `${firstName}`);
const userNames_2 = loggedin.map(({ lastName }) => `${lastName}`);
const userFirst = userNames_1.toString();
const userLast = userNames_2.toString();
updateData(loggedin_id, userFirst, userLast);
}
else {
alert("User Does not Exist.");
}
}
}
useEffect(() => {
gatherData();
console.log('useEffect role: ', role);
console.log('useEffect firstname: ', entryfirstName);
console.log('useEffect lastname: ', entrylastName);
}, [role, entryfirstName, entrylastName]);
const handleClick = (event) => {
event.preventDefault();
submit();
console.log('role: ', role);
console.log('firstname: ', entryfirstName);
console.log('lastname: ', entrylastName);
login(entryfirstName, entrylastName, email_input, password_input, role);
}
Here is code for userProvider.js
import React from "react";
import UserContext from "./UserContext";
const UserProvider = ({ children }) => {
const [user, setUser] = React.useState({
firstName: '',
lastName: '',
email: '',
role: '',
auth: false
});
const login = (firstName, lastName, email, password, role) => {
setUser((user) => ({
firstName: firstName,
lastName: lastName,
email: email,
password: password,
role: role,
auth: true,
}));
};
const logout = () => {
setUser((user) => ({
firstName: '',
lastName: '',
email: '',
password: '',
role: '',
auth: false,
}));
};
return (
<UserContext.Provider
value={{
user,
login,
logout }}
>
{children}
</UserContext.Provider>
);
};
export default UserProvider;
Solution
First, in your context
I would put the call to the axios:
note: Why call all users? I think this is not right, maybe axios.get(
${baseURL}/user/${email});
for most specific user, best pratices security or for example:
const login = async (firstName, lastName, email, password, role) => {
if(firstName && lastName && email && password && role){
const { data } = await axios.post(session_url, {}, {
auth: {
email: email,
password: password
}
});
if(data){
setUser((user) => ({
firstName: data.user.firstName,
lastName: data.user.lastName,
email: data.user.email,
role: data.user.role,
auth: true,
}));
}
}
Please read: Auth - Axios
but let's continue
import {useState, useEffect} from "react";
import UserContext from "./UserContext";
const baseURL = 'http://samplesite.org/';
const UserProvider = ({ children }) => {
const [entryuser, setEntryUser] = useState({});
const [user, setUser] = useState({
firstName: '',
lastName: '',
email: '',
role: '',
auth: false
});
// you need a useEffect to verify if user is logged
useEffect(() => {
const getCurrentUser = localStorage.getItem("currentUser");
if (getCurrentUser) {
const currentUser = JSON.parse(getCurrentUser);
// now you need validate expireAt
// also make an axios call here, to check if you are authenticated on the backend
// validate all necessary
setUser((user) => ({
firstName: currentUser.firstName,
lastName: currentUser.lastName,
email: currentUser.email,
role: currentUser.role,
auth: true,
}));
}
},[])
const login = async (firstName, lastName, email, password, role) => {
try {
const userlogins = await axios.get(`${baseURL}/user`);
if(userLogins.data) {
setEntryUser(userlogins.data.users);
const loggedin = entryuser.filter(obj => obj.email === email);
const loggedin_role = loggedin.map(({ role }) => `${role}`);
const loggedin_id = loggedin_role.toString();
const userNames_1 = loggedin.map(({ firstName }) => `${firstName}`);
const userNames_2 = loggedin.map(({ lastName }) => `${lastName}`);
const userFirst = userNames_1.toString();
const userLast = userNames_2.toString();
// you need save this data in cookies or localstorage,
// I use crypto-js in localStorage
const currentUser = {
firstName: userFirst,
lastName: userLast,
role: loggedin_id,
expireAt: // here you need a time to expire sesion
}
localStorage.setItem("currentUser", JSON.stringify(currentUser));
setUser((user) => ({
firstName: userFirst,
lastName: userLast,
email: email,,
role: loggedin_id,
auth: true,
}));
};
}
catch (error) {
console.log(error);
}
};
const logout = () => {
setUser((user) => ({
firstName: '',
lastName: '',
email: '',
password: '',
role: '',
auth: false,
}));
};
return (
<UserContext.Provider
value={{
user,
login,
logout }}
>
{children}
</UserContext.Provider>
);
};
export default UserProvider;
Second in your component
Note: Why does your login ask for your name and surname and role? It is better to handle this data from the backend and you could use only email and password
interface IFormLogin {
email: string;
password: string;
firstName: string;
lastName: string;
role: string;
}
export default function SignIn() {
const { login } = useContext(UserContext);
const [formLogin, setFormLogin] = useState<IFormLogin>({
email: "";
password: "";
firstName: "";
lastName: "";
role: "";
})
const handleInputChange = event => {
setFormLogin((formState: IFormLogin) => ({
...formState,
[event.target.name]: event.target.value,
},
}));
};
const handleSubmit = (event) => {
event.preventDefault();
const {email, password, firstName, lastName, role} = formLogin;
if (!email && !password && !firstName && !lastName && !role) {
alert("Enter all data");
return;
}
login(firstName, lastName, email, password, role);
}
return(
<form onSubmit={handleSubmit}>
<input name="email" type="email" value={formLogin.email || ""} onChange={handleInputChange} />
<input name="password" value={formLogin.password || ""} onChange={handleInputChange} />
<input name="firstName" value={formLogin.firstName || ""} onChange={handleInputChange} />
<input name="lastName" value={formLogin.lastName || ""} onChange={handleInputChange} />
<input name="role" value={formLogin.role || ""} onChange={handleInputChange} />
<button type="submit">Login</button>
</form>
);
Now if you need user data in other component
const { user } = useContext(UserContext);
console.log(user.firstName);
Recommendations
Please DON'T SAVE the password anywhere in the client, I mean localstorage, session storage, cookies, context, redux, etc.
You can use Json web token to encrypt data from the backend and that token save in localstorage.
Note: I use TypeScript in any case, please omit that part if you want.
Answered By - Camilo Gomez
Answer Checked By - - Dawn Plyler (ReactFix Volunteer)