react17+redux+react-router-dom实现路由拦截登录

2 篇文章 0 订阅
1 篇文章 0 订阅

功能介绍

redux创建redux store实例,react-redux监听store状态的改变,更新视图
logger记录action store状态改变,并在控制台打印日志信息
mockjs+axios模块http请求,实现登录用户信息获取
react-router-dom路由拦截,判断未登录从定向到Login页面

相关依赖

redux
react-redux
redux-logger
react-router-dom
mockjs
axios

redux执行示意图

在这里插入图片描述

创建全局redux store实例

import { createStore, combineReducers, applyMiddleware } from "redux";
import logger from "redux-logger";
import storage from "../utils/storage";
import { authenticatedSuccess } from "../utils/session";
export interface ActionType {
	type: string;
	payload?: any;
}
export interface UserInfoType {
	username?: string;
	phone?: string;
	userId?: number | string;
	token?: string;
	age?: number;
}
// 获取用户信息初始值
const storageUserInfo: UserInfoType = storage.getItem("userInfo") || {};
const login = (userInfo: UserInfoType = storageUserInfo, action: ActionType) => {
	switch (action.type) {
		case "LOGIN_REQUEST":
			return { ...action.payload };
		case "LOGIN_SUCCESS":
			authenticatedSuccess(action.payload.token);
			storage.setItem("userInfo", action.payload);
			return { ...action.payload };
		case "LOGIN_FAIL":
			return { ...action.payload };
		default:
			return userInfo;
	}
};
// combineReducers把一个有多个不同reducer函数作为value的object,合并成一个最终的reducer函数
const reducer = combineReducers({ login });
// createStore创建一个redux store实例,存放应用中说有的state,store唯一
// 引入中间件,包装store的dispatch方法以达到想要的目的
// logger中间件,state状态改变时在控制台打印日志
const store = createStore(reducer, applyMiddleware(logger));
export default store;

挂载组件 全局派发redux store实例

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import store from "./store";
import { Provider } from "react-redux";
import App from "./App";
import "./Mock";
const rootElement = document.getElementById("root");
ReactDOM.render(
	// Provider派发store实例
	<Provider store={store}>
		<StrictMode>
			<App />
		</StrictMode>
	</Provider>,
	rootElement
);

App组件入口

路由入口,实现全局路由拦截
import { Suspense } from "react";
import { BrowserRouter as Router, Redirect, Route, Switch } from "react-router-dom";
import PrivateRoute from "./routes/PrivateRoute";
import Login from "./pages/Login";
import Container from "./layout/Container";
import { isAuthenticated } from "./utils/session";
import "./styles.css";
const App = (props: any) => {
	return (
		<Suspense fallback={<div>loading...</div>}>
			<Router basename="example">
				<Switch>
					<Route
						path="/login"
						exact
						render={(props) => {
							return !!isAuthenticated() ? (<Redirect to="/" />) : (<Login {...props} />);
						}}
					/>
					<PrivateRoute path="/" component={Container} />
				</Switch>
			</Router>
		</Suspense>
	);
};
export default App;

PrivateRoute

自定义Route路由组件,判断当前是否已登录,进行路由重定向
import { Redirect, Route } from "react-router-dom";
import { isAuthenticated } from "../utils/session";

const PrivateRoute = (props: any) => {
	const { component: Component, path } = props;
	return (
		<Route
			path={path}
			render={(props) => {
				return !!isAuthenticated() ? (<Component {...props} />) : (<Redirect to="/login" />);
			}}
		/>
	);
};
export default PrivateRoute;

Sidebar

左侧菜单,点击跳转不同页面
import { NavLink } from "react-router-dom";
import routes, { RouteType } from "../../routes";
import layoutStyle from "../index.module.scss";
const Item = (props: any) => {
	const { name, path } = props;
	return (
		<NavLink
			className={layoutStyle.navlink}
			activeClassName={layoutStyle.active}
			to={path}>
			{name}
		</NavLink>
	);
};
const Menu = () => {
	return (
		<ul>
			{routes.map((route: RouteType, index: number) => {
				return (
					<li key={index}>
						<Item {...route} />
					</li>
				);
			})}
		</ul>
	);
};
const Sidebar = () => {
	return (
		<div className={layoutStyle.sidebar}>
			<h1>Sidebar</h1>
			<Menu />
		</div>
	);
};
export default Sidebar;

Container

登录成功后PrivateRoute指向的组件
import { lazy } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import PrivateRoute from "../../routes/PrivateRoute";
import Footer from "../Footer";
import Header from "../Header";
import Sidebar from "../Sidebar";
import Home from "../../pages/Home";
import Center from "../../pages/Center";
import layoutStyle from "../index.module.scss";
const Container = (props: any) => {
	return (
		<>
			<Header />
			<div className={layoutStyle.content}>
				<Sidebar />
				<div>
					<Switch>
						<PrivateRoute path="/home" exact auth component={Home} />
						<PrivateRoute path="/center" exact auth component={Center} />
						<Redirect exact to="/home" />
					</Switch>
				</div>
			</div>
			<Footer />
		</>
	);
};
export default Container;

Login

axios+mockjs模拟接口请求登录
import { useState } from "react";
import { connect } from "react-redux";
import http from "../../fetch/request";
const Login = (props: any) => {
	let [username, setUsername] = useState<string>("");
	let [password, setPassword] = useState<string>("");
	let [loading, setLoading] = useState<string>("");
	const handleLogin = () => {
		const { history, loginRequest, loginSuccess, loginFail } = props;
		loginRequest();
		setLoading("正在登录...");
		const data = { username, password };
		http("post", "/login", {}, data).then((res) => {
			const data = res.data;
			loginSuccess(data);
			setLoading("登录成功");
			setTimeout(() => {
				history.push("/home");
			}, 200);
		}).catch((e) => {
			loginFail(e);
			setLoading("登录失败, " + e);
		});
	};
	const handleSubmit = (e: any) => {
		e.preventDefault();
		if (!username || !password) {
			alert("请输入用户名和密码");
			return;
		}
		handleLogin();
	};
	return (
		<div style={{ margin: "20px" }}>
			<h1>登 录</h1>
			<form onSubmit={handleSubmit}>
				<input
					value={username}
					onChange={(e) => setUsername(e.target.value)}
					placeholder="please input username" />
				<br />
				<input
					value={password}
					onChange={(e) => setPassword(e.target.value)}
					placeholder="please input password" />
				<br />
				<div>
					<p>用户名:admin</p>
					<p>密码:111111</p>
				</div>
				<button type="submit">登 录</button>
				<div>{loading}</div>
			</form>
		</div>
	);
};
// 返回一个纯对象,该对象来自store中的state对象属性,合并到当前组件的props;通过props.stateName调用
const mapStateToProps = (state: any) => {
	return {};
};
// 返回一个纯对象,该对象来自store中的dispatch,注入到当前组件的props中;通过props.dispatchName调用
const mapDispatchToProps = (dispatch: any) => {
	return {
		loginRequest: () => dispatch({ type: "LOGIN_REQUEST" }),
		loginSuccess: (data: any) => dispatch({ type: "LOGIN_SUCCESS", payload: data }),
		loginFail: (msg: any) => dispatch({ type: "LOGIN_FAIL", payload: msg })
	};
};
// 函数将被调用两次。第一次是设置参数,第二次是组件与 Redux store 连接
// connect 函数不会修改传入的 React 组件,返回的是一个新的已与 Redux store 连接的组件,export这个新组件
export default connect(mapStateToProps, mapDispatchToProps)(Login);

Center

登录后在该页面展示全局状态的userInfo信息
import { connect } from "react-redux";
import { logout } from "../../utils/session";
const Center = (props: any) => {
	const { history, userInfo } = props;
	// 退出登录
	const handleLogout = () => {
		logout();
		history.replace("/login");
	};
	return (
		<div>
			<h1>Center</h1>
			<div>
				<div>姓名:{userInfo.username}</div>
				<div>年龄:{userInfo.age}</div>
				<div>手机号:{userInfo.phone}</div>
			</div>
			<div>
				<button onClick={handleLogout}>退出</button>
			</div>
		</div>
	);
};
// return store中的login信息,合并到组件的props
const mapStateToProps = (state: any) => {
	return {
		userInfo: state.login
	};
};
export default connect(mapStateToProps)(Center);

完整代码

效果图

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值