此示例演示了一个包含三个页面的简单登录流程:公共页面、受保护页面和登录页面。 为了查看受保护的页面,你必须先登录。
首先,访问公共页面。 然后,访问受保护的页面。 你尚未登录,因此你将被重定向到登录页面。 登录后,你将被重定向回受保护的页面。

封装 context 包裹容器

首先封装authprovider组件,利用context特性共享那些对于一个组件树而言是“全局”的数据。
全局定义usersigninsignout数据和方法,signinsignout使用了高阶函数,也方便后续扩展和修改。

context主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context更好的解决方案。

import { reactnode, createcontext, usestate } from "react";

export interface authcontexttype {
  user: any;
  signin: (user: string, callback: voidfunction) => void;
  signout: (callback: voidfunction) => void;
}

export let authcontext = createcontext<authcontexttype | null>(null);

const fakeauthprovider = {
  isauthenticated: false,
  signin(callback: voidfunction) {
    this.isauthenticated = true;
    settimeout(callback, 100);
  },
  signout(callback: voidfunction) {
    this.isauthenticated = false;
    settimeout(callback, 100);
  },
};

const authprovider = ({ children }: { children: reactnode }) => {
  const [user, setuser] = usestate<any>(null);

  let signin = (newuser: string, callback: voidfunction) => {
    return fakeauthprovider.signin(() => {
      setuser(newuser);
      callback();
    });
  };

  let signout = (callback: voidfunction) => {
    return fakeauthprovider.signout(() => {
      setuser(null);
      callback();
    });
  };

  return (
    <authcontext.provider value={{ user, signin, signout }}>
      {children}
    </authcontext.provider>
  );
};

export default authprovider;

封装 layout 父级容器

layout组件主要是针对登录状态进行校验,然后做相应处理。利用react-router v6中<outlet />组件显示嵌套路由,相比于v5版本v6实现嵌套路由更加方便,省略了很多冗余的判断代码。

import { usecontext } from "react";
import { usenavigate, link, outlet } from "react-router-dom";
import { authcontext, authcontexttype } from "../authprovider";

const useauth = () => usecontext(authcontext);

const authstatus = () => {
  let auth = useauth();
  let { user, signout } = auth as authcontexttype;
  let navigate = usenavigate();

  if (!user) return <p>没有登录</p>;
  return (
    <>
      <p>你好 {user}! </p>
      <button onclick={() => signout(() => navigate("/"))}>退出</button>
    </>
  );
};

const layout = () => {
  return (
    <div>
      <authstatus />
      <ul>
        <li>
          <link to="/">公共页面</link>
        </li>
        <li>
          <link to="/protected">受保护页面</link>
        </li>
      </ul>
      <outlet />
    </div>
  );
};

export default layout;

开发 login 模块

import { usecontext, formevent } from "react";
import { usenavigate, uselocation, location } from "react-router-dom";
import { authcontext, authcontexttype } from "../authprovider";

interface state extends omit<location, "state"> {
  state: {
    from: {
      pathname: string;
    };
  };
}

const useauth = () => usecontext(authcontext);

const login = () => {
  let auth = useauth();
  let { signin } = auth as authcontexttype;
  const { state } = uselocation() as state;
  let from = state.from.pathname || "/";
  let navigate = usenavigate();

  const handlesubmit = (event: formevent<htmlformelement>) => {
    event.preventdefault();

    let formdata = new formdata(event.currenttarget);
    let username = formdata.get("username") as string;

    signin(username, () => navigate(from, { replace: true }));
  };

  return (
    <div>
      <p>您必须登录才能查看该页面 {from}</p>

      <form onsubmit={handlesubmit}>
        <label>
          用户名: <input name="username" type="text" />
        </label>
        <button type="submit">登录</button>
      </form>
    </div>
  );
};

export default login;

开发 protected 包裹容器

主要就是对登录状态进行校验,成功则渲染子组件,否则跳转回登录页面

import { usecontext } from "react";
import { uselocation, navigate } from "react-router-dom";
import { authcontext, authcontexttype } from "../authprovider";

const useauth = () => usecontext(authcontext);

const requireauth = ({ children }: { children: jsx.element }) => {
  let auth = useauth();
  let { user } = auth as authcontexttype;
  let location = uselocation();

  if (!user) return <navigate to="/login" state={{ from: location }} replace />;

  return children;
};

export default requireauth;

app 入口文件

入口文件没有对路由进行懒加载优化,因为是小应用,所以实际开发还是要考虑性能优化的。

import { routes, route } from "react-router-dom";

import authprovider from "src/views/authprovider";
import layout from "src/views/auth/layout";
import loginpage from "src/views/auth/login";
import publicpage from "src/views/auth/publicpage";
import requireauth from "src/views/auth/requireauth";
import protectedpage from "src/views/auth/protectedpage";

const app = () => {
  return (
    <authprovider>
      <routes>
        <route element={<layout />}>
          <route path="/" element={<publicpage />} />
          <route path="/login" element={<loginpage />} />
          <route
            path="/protected"
            element={
              <requireauth>
                <protectedpage />
              </requireauth>
            }
          />
        </route>
      </routes>
    </authprovider>
  );
};

export default app;

到此这篇关于浅谈react-router v6 实现登录验证流程的文章就介绍到这了,更多相关react-router登录验证内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!