书接上文,我们还有很多功能没有实现,比如说我们经常会用到路由守卫控制登录、注销等,在react-router6中如何使用呢?
我们看一下代码。
js
export default function App() {
return (
<div className="app">
<AuthProvider>
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="product" element={<Product />}>
<Route path=":id" element={<ProductDetail />} />
</Route>
<Route
path="user"
element={
<RequiredAuth>
<User />
</RequiredAuth>
}
/>
<Route path="login" element={<Login />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</Router>
</AuthProvider>
</div>
);
}
function Layout() {
return (
<div className="border">
<Link to="/">首页</Link>
<Link to="/product">商品</Link>
<Link to="/user">用户中心</Link>
<Link to="/login">登录</Link>
<Outlet />
</div>
);
}
function Home() {
return (
<div>
<h1>Home</h1>
</div>
);
}
function Product() {
return (
<div>
<h1>Product</h1>
<Link to="/product/123">商品详情</Link>
<Outlet />
</div>
);
}
function ProductDetail() {
let navigate = useNavigate();
const params = useParams();
return (
<div>
<h1>ProductDetail</h1>
<p>{params.id}</p>
<button onClick={() => navigate("/")}>go home</button>
</div>
);
}
function RequiredAuth({ children }) {
const auth = useAuth();
const location = useLocation();
if (!auth.user) {
return <Navigate to={"/login"} state={{ from: location }} replace={true} />;
}
return children;
}
function User() {
const auth = useAuth();
const navigate = useNavigate();
return (
<div>
<h1>User</h1>
<p>{auth.user?.username}</p>
<button
onClick={() => {
auth.signout(() => navigate("/login"));
}}
>
退出登录
</button>
</div>
);
}
function Login() {
const auth = useAuth();
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from.pathname || "/";
if (auth.user) {
return <Navigate to={from} />;
}
const submit = (e) => {
const formData = new FormData(e.currentTarget);
const username = formData.get("username");
auth.signin({ username }, () => {
navigate(from, { replace: true });
});
};
return (
<div>
<h1>Login</h1>
<form onSubmit={submit}>
<input type="text" name="username" />
<button type="submit">login</button>
</form>
</div>
);
}
function NoMatch() {
return (
<div>
<h1>404</h1>
</div>
);
}
----------------文件分割------------------------
const AuthContext = React.createContext();
export function AuthProvider({ children }) {
const [user, setUser] = React.useState(null);
const signin = (newUser, callback) => {
setUser(newUser);
callback();
};
const signout = (callback) => {
setUser(null);
callback();
};
let value = {
user,
signin,
signout,
};
return <AuthContext.Provider value={value} children={children} />;
}
export function useAuth() {
return React.useContext(AuthContext);
}
在RequiredAuth中,我们做了一次判断,判断当前是否登录
js
function RequiredAuth({ children }) {
const auth = useAuth();
const location = useLocation();
if (!auth.user) {
return <Navigate to={"/login"} state={{ from: location }} replace={true} />;
}
return children;
}
Navigate组件是做什么呢?我们可以通过参数得知,它的作用是跳转,是不返回别的东西的。接收什么参数呢?{to, state, replace},他们都是做什么的?
to -- 向哪里跳转
state -- 传递下去的数据
replace -- 到底是history.push一个记录还是history.replace一个记录
为什么要replace替换?因为我明明登录了,点击回退又回到登录页的行为很弱智。
js
export default function Navigate({to,state,replace}) {
const navigate = useNavigate()
useEffect(() => {
navigate(to, { replace, state });
});
return null
}
并且我们重新修改了useNavigate,让它适配更多功能,而不是简单的to。
js
export function useNavigate(){
//跳转
const { navigator } = React.useContext(NavigationContext);
const navigate = React.useCallback(
(to, options = {}) => {
if (typeof to === "number") {
navigator.go(to);
return;
}
(!!options.replace ? navigator.replace : navigator.push)(
to,
options.state
);
},
[navigator]
);
return navigate;
}
这样我们既可以使用usenavigate路由跳转,也可以使用Link标签和Navigate组件进行路由跳转了!
我们已经拥有了一个简单的react-router6,当然这不是全部,以后有机会再继续。