功能介绍
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;
}
};
const reducer = combineReducers({ login });
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={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>
);
};
const mapStateToProps = (state: any) => {
return {};
};
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 })
};
};
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>
);
};
const mapStateToProps = (state: any) => {
return {
userInfo: state.login
};
};
export default connect(mapStateToProps)(Center);
效果图