0

I am trying to setup a context in React to handle user information which will be called once a user has logged in to the system. However, I am having trouble setting the user in the first place.

As it is, the user will enter their login info, and selected a role from a dropdown list. The API then returns a result based on the login info. I am trying to update the user context based on that result, but the user value in UserContext just ends up as undefined when I try to set it with useUserUpdate? what am I doing wrong here?

I have tried making the custom hook, useUserUpdate, to handle manipulating the user state, but it is not working and I am rather lost. I think this is where the issue is coming from as I have console.log all over the place and have seen that when calling this hook is when user becomes undefined. Any help that could be provided would be greatly appreciated.

App.js

import React from "react";
import { useUser } from "./contexts";
import { UserContextProvider } from "./contexts";

//import page components
import LoginForm from "./sharedComponents/LoginForm";
import WorkflowTemplate from "./workflows/WorkflowTemplate";
import "./App.css";

function App() {
    const user = useUser();

    return (
        <UserContextProvider>
            <div className="App">
                {user != null ? <WorkflowTemplate /> : <LoginForm />}
            </div>
        </UserContextProvider>
    );
}

export default App;


contexts.js

import React, { createContext, useState, useContext } from "react";
import Cookies from "universal-cookie";

const UserContext = createContext();
const UpdateUserContext = createContext();

const Cookie = new Cookies();

export const useUser = () => {
    return useContext(UserContext);
};

export const useUserUpdate = () => {
    return useContext(UpdateUserContext);
};

export const UserContextProvider = ({ children }) => {
    const [user, setUser] = useState({
        email: "test",
        name: "",
        id: "",
        role: "",
    });
    console.log(user);

    const updateUser = ({ user }) => {
        setUser({ user });
        console.log("user passed to context prov");
        console.log(user);
    };

    return (
        <UserContext.Provider value={user}>
            <UpdateUserContext.Provider value={updateUser}>
                {children}
            </UpdateUserContext.Provider>
        </UserContext.Provider>
    );
};

LoginForm.js

import React, { useState, useEffect } from "react";
import {
    i_sAccessToken,
    i_sGetLoggedInUser,
    i_sGETOrgs,
} from "../publicFunctions/identity_serviceAPI";
import { useUser, useUserUpdate } from "../contexts";
import Cookies from "universal-cookie";
import "../App.css";

function LoginForm() {
    const Cookie = new Cookies();
    const user = useUser();
    const userUpdate = useUserUpdate();
    const [details, setDetails] = useState({
        email: "",
        role: "",
        password: "",
    });
    const submitHandler = (event) => {
        event.preventDefault();
        Login(details);
        setDetails({ email: "", password: "", role: "" });
    };

    const Login = (details) => {
        if (details.email === "") return alert("No email entered");
        if (details.password === "") return alert("No password entered");
        if (details.role === "") return alert("Please select a role");

        //call API here to check username and save JWT
        i_sAccessToken(details).then((result) => {
            if (result.detail === "Incorrect email or password") {
                alert("Incorrect email or password");
            } else {
                //if result does not return error set cookie
                Cookie.set("JWT", result.access_token);
                //get USER info from API
                i_sGetLoggedInUser().then((result) => {
                    userUpdate({
                        ...user,
                        email: result.email,
                        name: result.name,
                        id: result.id,
                        role: details.role,
                    });
                });
            }
        });
    };

    return (
        <form onSubmit={submitHandler}>
            <div className="form-inner">
                <h2>Login</h2>
                {/*{error != "" ? <div className="error">{error} </div> : ""}*/}

                <div className="form-group">
                    <label htmlFor="email">Email: </label>
                    <input
                        type="text"
                        name="name"
                        id="name"
                        onChange={(event) =>
                            setDetails({ ...details, email: event.target.value })
                        }
                        value={details.email}
                    />
                </div>
                <div className="form-group">
                    <label htmlFor="password">Password: </label>
                    <input
                        type="password"
                        password="password"
                        id="password"
                        onChange={(event) =>
                            setDetails({ ...details, password: event.target.value })
                        }
                        value={details.password}
                    />
                </div>
                <div>
                    <label htmlFor="role">Enter Role: </label>
                    <select
                        onChange={(event) =>
                            setDetails({ ...details, role: event.target.value })
                        }
                        value={details.role}
                    >
                        <option value="">Select Role</option>
                        <option value="Analyst">Analyst</option>
                        <option value="Manager">Manager</option>
                        <option value="Sample admin">Sample admin</option>
                        <option value="Developer">Developer</option>
                    </select>
                </div>
                <input type="submit" value="LOGIN" />
            </div>
        </form>
    );
}

export default LoginForm;




Greg
  • 35
  • 1
  • 2
  • 6

2 Answers2

0

You defined the handler as taking an object as an argument that contains the user but when calling the userUpdate function you pass the user directly:

// this function expects an object that has a property user
const updateUser = ({ user }) => {
    // this will again set an object in the state that contains the user rather than the user itself
    setUser({ user });
    // ....
};

// here you pass the user directly
userUpdate({
    ...user,
    email: result.email,
    name: result.name,
    id: result.id,
    role: details.role,
});

Also there is no reason to have two separate contexts for the user and the updater function. You can have both in the same context that has both the user and the updater function in its value.

const UserContext = createContext();

const initialUser = {
    email: "test",
    name: "",
    id: "",
    role: "",
}

export const useUser = () => useContext(UserContext);

export const UserContextProvider = ({ children }) => {
    const userState = useState(initialUser);

    return (
        <UserContext.Provider value={userState}>  
            {children}
        </UserContext.Provider>
    );
};

You can then simply use it like:

const [user, updateUser] = useUser();
trixn
  • 15,761
  • 2
  • 38
  • 55
  • Thank you for your help, I removed the {} from the function argument and it is working now :) will try and clean up the context with your other suggestions.Thanks! – Greg Mar 26 '21 at 09:49
  • @Greg Glad it helps you. Please vote up and/or mark it as the accepted answer if you think it solved your problem. – trixn Mar 26 '21 at 09:50
0

useUser must call on children of where is UserContextProvider used

ex:

<UserContextProvider>
  <User />
</UserContextProvider>

Users.js

function Users() {
    const user = useUser();

    return (
         <div className="App">
                {user != null ? <WorkflowTemplate /> : <LoginForm />}
            </div>
    );
}
tuvudu
  • 109
  • 1
  • 4