0

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;

1 Answers1

0

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.

JWT Authentication in React

Note: I use TypeScript in any case, please omit that part if you want.

Camilo Gomez
  • 165
  • 1
  • 6