reactJS 中的 useContext 带有导致未定义对象的自定义挂钩

useContext in reactJS with custom hook leading to undefined object

我正在尝试在 React 中设置一个上下文来处理用户信息,一旦用户登录到系统就会调用该上下文。但是,我一开始就无法设置用户。

实际上,用户将输入他们的登录信息,并从下拉列表中选择一个角色。 API 然后 returns 基于登录信息的结果。我正在尝试根据该结果更新用户上下文,但是当我尝试使用 useUserUpdate 设置它时,UserContext 中的用户值最终变为未定义?我在这里做错了什么?

我尝试制作自定义挂钩 useUserUpdate 来处理用户状态操作,但它不起作用,我很迷茫。我认为这就是问题所在,因为我到处都是 console.log 并且已经看到调用此挂钩时用户变得未定义。如果能提供任何帮助,我们将不胜感激。

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;




您将处理程序定义为将一个对象作为包含用户的参数,但是在调用 userUpdate 函数时您直接传递了 user

// 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,
});

此外,没有理由为用户和更新程序函数设置两个单独的上下文。您可以在同一上下文中同时拥有用户和更新程序功能。

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>
    );
};

然后您可以像这样简单地使用它:

const [user, updateUser] = useUser();

useUser 必须调用 children UserContextProvider 使用的地方

例如:

<UserContextProvider>
  <User />
</UserContextProvider>

Users.js

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

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