使用React Context全局注入用户认证信息

继续记录自己学习React的心得

React Context

Context的功能简单地说就是可以将一些数据注入到Context对象中,使其下辖的组件可以随时随地访问这些数据,省去了逐层传递的步骤。

相对于在组件里挖槽(比如{props.children}),使用Context应该更注重随时随地都可能有需求使用这些数据这个目的。

用到的两个核心API:

  • React.createContext(defaultValue)
    创建Context对象,可传入defaultValue或undefined。defaultValue的作用官网有介绍,即:当某个组件订阅了Context但未匹配到Provider时,就会访问defaultValue值,当传入undefined时,defaultValue无效。
  • Context.Provider
    Context对象都会返回Provider组件,其下的组件会订阅Provider中的数据。Provider接收value属性,用于将value传递给消费组件,当value发生变化时所有消费组件都会重新渲染。

使用React Context全局注入用户认证信息

内容衔接之前的用户认证模块。首先需要明确为何需要全局注入用户认证信息,其实一个最简单的答案就是:之前的用户认证模块只提供了用户认证的方法,并没有提供存储用户认证信息的功能,而用户认证信息又是一个大部分组件都有可能使用的数据,所以将其注入到Context中是很有意义的。

目前所构建的用户认证模块大致包括下列内容:

复制代码
// User接口
export interface User {
  id: string;
  name: string;
  email: string;
  title: string;
  organization: string;
  token: string;
}
// 获取token
export const getToken = () => { ... };
// 登录
export const login = (param) => { ... };
// 注册
export const register = (param) => { ... };
// 登出
export const logout = () => { ... };
复制代码

下面开始构建AuthContext模块:

该模块包括三大内容:Context对象创建、Provider组件构建、useContext Hook调用。
Context对象需要传入用户认证模块所有的必要数据和方法,如下

复制代码
const AuthContext = React.createContext<
  | {
      user: User | null;
      login: (form: AuthForm) => Promise<void>;
      register: (form: AuthForm) => Promise<void>;
      logout: () => Promise<void>;
    }
  | undefined
>(undefined);
// 更改Context名称
AuthContext.displayName = "AuthContext";
复制代码

注意login、register、logout返回的都是fetch()
Provider组件构建涉及到用户认证信息初始化、异步请求状态管理。
用户认证信息初始化主要是需要读取token,用以保持用户登录状态,如下

复制代码
import * as auth from "用户认证模块";

const boostrapUser = async () => {
  let user = null;
  const token = auth.getToken();
  if (token) {
    const data = await fetch(...) // 验证用户身份
    user = data.user;
  }
  return user;
};
复制代码

请求状态管理主要是将异步请求状态管理和Provider结合,如下

复制代码
  const {
    data: user,
    error,
    run,
    isIdle,
    isError,
    isLoading,
    setData: setUser,
  } = useAsync<User | null>();

// 重写用户认证模块的几个函数,将其与异步请求管理相结合
  const login = (form: AuthForm) => auth.login(form).then(setUser);
  const register = (form: AuthForm) => auth.register(form).then(setUser);
  const logout = () => auth.logout().then(() => setUser(null));

// Provider初次渲染时先初始化用户认证信息
  useEffect(() => {
    run(boostrapUser());
  }, []);
//
isLoading, isIdle, isError可用于呈现Provider组件不同状态
return (
<AuthContext.Provider
children={children}
value={{ user, login, register, logout }} // 传入Context所需的value
/>
);
复制代码

useContext Hook的便是提供给其他组件访问Provider属性的一个简单方法,用法如下

export const useAuth = () => {
  const context = React.useContext(AuthContext);
  if (!context) { // 必须保证context对象存在
    throw new Error("useAuth必须在AuthProvider中使用");
  }
  return context;
};

AuthProvider用法

举例在login页面的简单用法:

复制代码
export const LoginScreen = () => {
  const { user, login } = useAuth();
  const { run, isLoading, error } = useAsync();

  const handleSubmit = async (values: {
    username: string;
    password: string;
  }) => {
    await run(login(values)); // 可全局调用login
  };

  return (
    <Form onFinish={handleSubmit}>
      {user ? ( // 全局读取user,且状态都是一致的
          登录成功,用户:{user?.name}
      ) : null}
      ......
      <Form.Item>
        <LongButton loading={isLoading} htmlType={"submit"} type={"primary"}>
          登录
        </LongButton>
      </Form.Item>
    </Form>
  );
};
复制代码

 

posted @   晚安NN  阅读(591)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示