React设计用户登录系统

目录

  • 前言
  • 一、需求分析
    • 1.1 AuthenticatedApp
    • 1.2 UnauthenticatedApp
  • 二、框架
  • 三、实现细节
    • 3.1 使用context管理用户信息
    • 3.2 实现UnauthenticatedApp
    • 3.2.1 LoginScreen
    • 3.2.1 RegisterScreen
    • 3.3 实现AuthenticatedApp
    • 3.4 实现login/register的请求
    • 3.5 与localstorage的交互
    • 完善AuthProvider
  • 四、总结

前言

最近开始学习React,跟着Nolan老师开发Jira,有很多干货,这里把课程的第5章用户认证实践做一个总结,我把我的代码都放在codesandbox上了,修改了部分老师的代码


一、需求分析

1.1 AuthenticatedApp

如果用户登录了,就显示AuthenticatedApp,为了方便,页面显示Hello World以及有个logout button

1.2 UnauthenticatedApp

如果用户没有登录,就显示UnauthenticatedApp,有login以及register的页面


二、框架

login框架大概如图,登录信息会存到localstorage中,然后通过context统一管理登录的信息
React设计用户登录系统_第1张图片


三、实现细节

3.1 使用context管理用户信息

context属于全局变量,所以需要在组件的外面

    <AppProviders>
      <App />
    </AppProviders>

其中AppProviders中包含了AuthProvider

export const AppProviders = ({ children }: { children: ReactNode }) => {
  return <AuthProvider>
    {children}
  </AuthProvider>;
};

AuthProvider组件就是提供Context的,给全局提供{ user, login, register, logout }

export const AuthProvider = ({ children }: { children: ReactNode }) => {

  //实现{ user, login, register, logout }
  ...
  
  return (
    <AuthContext.Provider
      children={children}
      value={{ user, login, register, logout }}
    />
  );
};

AuthProvider中使用到了React的context,其中用user, login, register, logout

const AuthContext = React.createContext<{
      user: User | null;
      login: (form: AuthForm) => Promise<void>;
      register: (form: AuthForm) => Promise<void>;
      logout: () => Promise<void>;
    } | undefined>(undefined);

那么有了AuthProvider了,其它组件使用可以通过useContext调用

	{ user, login, register, logout } =
	 React.useContext(AuthContext)

向外可以通过hook提供context

export const UseAuth = () => {
  const context = React.useContext(AuthContext);
  if (!context) throw new Error("UseAuth error");
  return context;
};

3.2 实现UnauthenticatedApp

这个页面就是用户可以选择登陆或者注册,点击可以切换页面

export const UnauthenticatedApp = () => {
  const [isLogin, setIsLogin] = useState(false);
  return (
    <div>
      {isLogin ? <LoginScreen /> : <RegisterScreen />}
      <button onClick={()=>setIsLogin(!isLogin)} >switch</button>
    </div>
  );
};

3.2.1 LoginScreen

登录页面是一个表单,实现比较简单,重点是使用UseAuth()

export const LoginScreen = () => {
  const { user, login } = UseAuth();

  if(user) {
    console.log('user has login', user)
  }

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const username = (event.currentTarget.elements[0] as HTMLInputElement)
      .value;
    const password = (event.currentTarget.elements[1] as HTMLInputElement)
      .value;
    console.log("useranme: ", username, "password: ", password);
    login({ username, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="username">用户名</label>
        <input type="text" id={"username"} />
      </div>
      <div>
        <label htmlFor="password">密码</label>
        <input type="password" id="password" />
      </div>
      <button type={"submit"}>登录</button>
    </form>
  );
};

3.2.1 RegisterScreen

注册页面跟登录页面非常像,就是使用UseAuth的register

export const RegisterScreen = () => {
  const {register} = UseAuth()
  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const username = (event.currentTarget.elements[0] as HTMLInputElement)
      .value;
    const password = (event.currentTarget.elements[1] as HTMLInputElement)
      .value;
    console.log("useranme: ", username, "password: ", password);
    register({username, password})
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="username">用户名</label>
        <input type="text" id={"username"} />
      </div>
      <div>
        <label htmlFor="password">密码</label>
        <input type="password" id="password" />
      </div>
      <button type={"submit"}>注册</button>
    </form>
  );
};

3.3 实现AuthenticatedApp

这个页面就是显示hello world并且有个退出键

export const AuthenticatedApp = () => {
  const { logout } = UseAuth();

  return (
    <div>
      <h1>hello world</h1>
      <button onClick={() => logout()}>logout</button>
    </div>
  );
};

3.4 实现login/register的请求

通过axios发送http的请求,上面提到的AuthProvider中可以直接调用这里的方法就行

interface data {
  username: string;
  password: string;
}

export const login = async (data: data) => {
  const loginUrl = `${apiUrl}/posts`;
  return axios.post(loginUrl);
};

export const register = async (data: data) => {
  const loginUrl = `${apiUrl}/posts`;
  return axios.post(loginUrl);
};

export const logout = async () => {};

3.5 与localstorage的交互

当用户登录或者注册,状态信息显示保存在localstorage的

const setUserToLocalstorage = (user: User) => {
  window.localStorage.setItem(
    localStorageUserKey,
    JSON.stringify({ id: user.id, token: user.token })
  );
};

const removeUserFromStorage = () => {
  window.localStorage.removeItem(localStorageUserKey)
}

const checkLocalstorage = () => {
  let storageUser = window.localStorage.getItem(localStorageUserKey);
  if (storageUser) return JSON.parse(storageUser);
};

完善AuthProvider

之前提到了AuthProvider会提供{ user, login, register, logout },没有具体讲到实现

这个组件需要在调用的时候先看看localstorage有没有信息,有的话先赋值user
在调用login时候把信息保存在localstorage上,logout把localstorage的信息移除

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState<User | null>(null);

  const login = (form: AuthForm) => {
    Auth.login(form).then((res) => {
      let loginUser = { id: res.data.id, token: res.data.id };
      setUserToLocalstorage(loginUser);
      setUser(loginUser);
    });
  };

  const register = (form: AuthForm) =>
    Auth.register(form).then((res) =>
      setUser({ id: res.data.id, token: res.data.id })
    );

  const logout = () =>
    Auth.logout().then(() => {
      removeUserFromStorage()
      setUser(null)
    });

  useEffect(() => {
    let cacheUser = bootstrapUser();
    setUser(cacheUser);
  }, []);

  return (
    <AuthContext.Provider
      children={children}
      value={{ user, login, register, logout }}
    />
  );
};

四、总结

实现用户登录是很多App都会有的功能,这里通过简单的页面展示实现了一个不错的架构,通过Context管理用户信息,然后使用UseAuth调用,较少了耦合度

你可能感兴趣的:(React)