成功调用API后无法更新上下文

时间:2020-05-24 06:50:46

标签: reactjs typescript next.js

我试图在NextJS中为我的项目实现一个简单的登录功能,并且试图使用Context API来存储用户的数据。从后端获取数据后,我尝试更新上下文,但未更新。尝试将其记录在控制台中时,它会显示一个空对象。

这是我尝试过的:

UserContextProvider.js

import React, { useState, createContext } from 'react';

export const UserContext = createContext();

const UserContextProvider = (props) => {
    const [user, setUser] = useState({ });

    const storeUser = new_user => {
        console.log("User: ", new_user);
        setUser({
            username: new_user.name,
            email: new_user.email,
            image: new_user.image_link
        });
    }

    const showUser = () => {
        console.log(user);
    }

    const logout = () => {
        setUser({});
    }

    return (
        <UserContext.Provider value={{ user, storeUser, showUser }}>
            {props.children}
        </UserContext.Provider>
    )
}

export default UserContextProvider

[provider] .tsx

import React, { useContext } from "react";
import { Container, CircularProgress, Typography } from "@material-ui/core";
import { makeStyles, Theme } from "@material-ui/core/styles";
import { } from "@material-ui/icons";
import Router from 'next/router'
import UserContextProvider, { UserContext } from '../../components/UserContextProvider';

const styles = makeStyles((theme: Theme) => ({
  root: {
    width: "100vw",
    height: "100vh",
    display: "flex",
    flexDirection: "column",
    justifyContent: "center",
    alignItems: "center",
  },
  title: {
    marginTop: theme.spacing(2),
  },
  error: {
    textAlign: "center",
  },
}));

interface Props {
  provider: string;
  code: string;
  classes: any;
}

function LoginPage(props: Props) {
  const [error, setError] = React.useState(false);

  const { storeUser, showUser } = useContext(UserContext);

  const googleLogin = () => {
    fetch("/api/login/google", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        code: props.code,
      }),
    })
      .then((resp) => resp.json())
      .then((res) => {
        fetch(`${process.env.NEXT_PUBLIC_BACKEND_URL}/account/social_login`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          mode: "cors",
          body: JSON.stringify({
            id_token: res.id_token,
            provider: "google",
          }),
        })
          .then((resp) => resp.json())
          .then((response) => {
            localStorage.token = response.token;
            document.cookie = `token=${response.token}; path=/; max-age=${
              60 * 60 * 24 * 100
              }`;
            storeUser({
              name: response.user.name,
              email: response.user.email,
              image_link: response.user.image_link
            });
            //This shows an empty object
            showUser();
            Router.push("/")
          })
          .catch((e) => {
            console.log(e);
            setError(true)
          });
      })
      .catch((err) => {
        console.log(err);
        setError(true)
      })
  }

  //This works
  React.useEffect(() => {
    storeUser({
      name: "wqygubcjqnc",
      email: "asbcascacsj",
      image_link: "asjbcjasnck"
    });
    showUser();
  });

  const facebookLogin = () => {
    fetch("/api/login/facebook", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        code: props.code,
      }),
    })
      .then((resp) => resp.json())
      .then((res) => {
        fetch(`${process.env.NEXT_PUBLIC_BACKEND_URL}/login`, {
          method: "POST",
          body: JSON.stringify({
            access_token: res.access_token,
            provider: "facebook",
          }),
        })
          .then((resp) => resp.json())
          .then((response) => {
            localStorage.token = response.token;
            window.location.href = "/";
          })
          .catch((e) => {
            console.log(e);
            setError(true)
          });
      })
      .catch((err) => {
        console.log(err);
        setError(true)
      });
  }

  React.useEffect(() => {
    if (props.provider == "google") googleLogin();
    else if (props.provider == "facebook") facebookLogin();
    else alert("Invalid login provider");
  }, [])

  const classes = styles()

  return (
    <UserContextProvider>
      <Container className={classes.root}>
        {error ? (
          <React.Fragment>
            <Typography className={classes.error} variant="h6" color="error">
              Something went wrong. Please try again. If the problem still
              exists, contact the administrator.
          </Typography>
          </React.Fragment>
        ) : (
            <React.Fragment>
              <CircularProgress size={"4rem"} />
              <Typography variant="h6" className={classes.title}>
                Logging you in
          </Typography>
            </React.Fragment>
          )}
      </Container>
    </UserContextProvider>
  );
}


export const getServerSideProps = async (context: any) => {
  return {
    props: {
      provider: context.params?.provider,
      code: context.query.code,
    },
  };
};

export default LoginPage

我目前仅使用Google登录名,因此请忽略Facebook登录功能。

1 个答案:

答案 0 :(得分:0)

实际上,上下文值未显示最新值,因为在设置上下文后立即记录上下文,该值不会更新, 每当您更新React Context时,所有使用该上下文的组件都将使用更新的context值重新呈现,因此我们可以做的是将逻辑分离到单独的处理程序中,当使用用户响应更新上下文时,这些处理程序将被调用useEffect

/* in Login page component*/

    const { user, storeUser, showUser } = useContext(UserContext);

    const handleLogin = () => {
       //any logic for handling login 
       showUser();
       Router.push("/")       
    }

    useEffect(() => {
        // will get called when the user response is updated
        handleLogin()
    }, [user]);

    // in similar way for error     
    useEffect(() => {
       setError(true)
    }, [error]);

  /*in function googleLogin just update the context value , 
     and the handling of the login functionality will be handled by the useEffect and 
     handleLogin function */

    const googleLogin = () => {
        //rest of the code  
         storeUser({
              name: response.user.name,
              email: response.user.email,
              image_link: response.user.image_link
         });
    }