在使用react开发项目中,有些页面需要登录之后才能访问,所以需要进行拦截,此处分享采用react-router-dom v4+redux+redux-saga+ant-mobile+axios技术来实现
Login.jsx
import React from "react";
import { is, fromJS } from "immutable";
import { connect } from "react-redux";
import { PropTypes } from "prop-types";
import { login } from "../../store/login/action";
import { InputItem, Button, List } from "antd-mobile";
class Login extends React.Component {
static propTypes = {
loginInfo: PropTypes.object.isRequired,
login: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = { name: "", psd: "" };
}
//antd-mobile的InputItem必须用List组件包裹,其输入值就是value
username = value => {
this.setState({
name: value
});
};
password = value => {
this.setState({
psd: value
});
};
toLogin = () => {
if (this.state.name === "") {
// to do sth
} else if (this.state.psd === "") {
// to do sth
} else {
let data = {};
data.username = this.state.name;
data.password = this.state.psd;
// 触发action
this.props.login(data);
}
};
componentWillReceiveProps(nextProps) {
if (Object.keys(nextProps.loginInfo).length > 0) {
if (nextProps.loginInfo.result.code === 10000) {
// 登录成功
sessionStorage.setItem(
"userinfo",
JSON.stringify(nextProps.loginInfo.data[0])
);
setTimeout(() => {
let RedirectUrl = nextProps.location.state
? nextProps.location.state.from.pathname
: "/";
nextProps.history.push(RedirectUrl);
}, 200);
} else {
}
}
}
shouldComponentUpdate(nextProps, nextState) {
return (
!is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
);
}
componentWillUpdate(nextProps, nextState) {}
componentWillMount() {}
render() {
return (
);
}
componentDidMount() {}
componentDidUpdate() {}
componentWillUnmount() {}
}
export default connect(
state => ({
loginInfo: state.loginInfo.userinfo
}),
{ login }
)(Login);
utils/asyncCompontent.jsx
import React from 'react'
export default function asyncComponent(importComponent){
class AsyncComponent extends React.Component{
constructor(props){
super(props);
this.state = {
component:null
};
}
async componentDidMount(){
const {default:component} = await importComponent();
this.setState({component});
}
render(){
const C = this.state.component;
return C ? : null;
}
}
return AsyncComponent;
}
router/index.js
import React from "react";
import { HashRouter, Route, Redirect, Switch } from "react-router-dom";
import { PrivateRoute } from "./auth"; //需要登录的路由
import asyncComponent from "../utils/asyncComponent";//按需加载
import app from "../App";
const example = asyncComponent(() =>import("../components/antiDesign/example"));
const login = asyncComponent(() => import("../components/login/login"));
const noMatch = asyncComponent(() => import("../components/noMatch/noMatch"));
class RouteConfig extends React.Component {
render() {
return (
);
}
}
export default RouteConfig;
router/auth.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
export const PrivateRoute = ({ component: ComposedComponent, ...rest }) => {
class Authentication extends Component {
render() {
let isLogin= this.props.isLogin
? this.props.isLogin
: sessionStorage.getItem("userinfo")
? sessionStorage.getItem("userinfo")
: "";
return (
!isLogin? (
) : (
)
}
/>
);
}
}
const AuthenticationContainer = connect(state => ({
isLogin: state.loginInfo.isLogin
}))(Authentication);
return ;
};
store/store.js
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import createSagaMiddleware from 'redux-saga'
import logger from 'redux-logger'
import * as login from './login/reducer'
import rootSaga from './sagas'
const sagaMiddleware = createSagaMiddleware();
const middlewares = [ sagaMiddleware, logger];
let store = createStore(
combineReducers(login),
composeWithDevTools(applyMiddleware(...middlewares))
);
sagaMiddleware.run(rootSaga);
export default store;
store/sagas/index.js
import { takeEvery, takeLatest, call, put, all } from 'redux-saga/effects';
import * as loginPro from '../login/action-types';
import { login } from '../../service/api';
// worker saga
// Login
function* getLoginInfo(action) {
try {
const userInfo = yield call(login, action.param);
if (userInfo.data.result.code === 10000) {
yield put({ type: loginPro.LOGIN_INFO, userinfo: userInfo.data, isLogin: true })
} else {
yield put({ type: loginPro.LOGIN_INFO, userinfo: userInfo.data, isLogin: false })
}
} catch (e) {
yield put({ type: loginPro.LOGIN_FAILIURE, error: e })
}
}
// wacther saga
function* takeLogin() {
yield takeLatest(loginPro.LOGIN_SUCCESS, getLoginInfo)
}
// root saga
export default function* rootSaga() {
yield takeLogin()
}
store/login/action-types.js
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_INFO = 'LOGIN_INFO';
export const LOGIN_FAILIURE = 'LOGIN_FAILIURE';
store/login/action.js
import * as pro from './action-types'
export const login = (param) => {
return (dispatch) => dispatch({
type: pro.LOGIN_SUCCESS,
param
})
}
store/login/reducers.js
import * as pro from './action-types'
let defaultState = {
userinfo: {},
error: {}
}
export const loginInfo = (state = defaultState, action) => {
switch (action.type) {
case pro.LOGIN_INFO:
return {...state, ...action }
case pro.LOGIN_FAILIURE:
return {...state, ...action }
default:
return state;
}
}
service/api.js
import {instance} from './apiConfig';
export const login = (data) => {
return instance.post('/login', data)
}
service/apiConfig.js
import axios from 'axios';
import Qs from 'qs';
import { Toast } from 'antd-mobile'
// 全局默认配置
// 设置 POST 请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// 在向服务器发送前,修改请求数据(只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法)
// 采用Qs的方式是为了用于application/x-www-form-urlencoded
axios.defaults.transformRequest = [(data) => { return Qs.stringify(data) }]
// 在传递给 then/catch 前,允许修改响应数据
// axios.defaults.transformResponse = [(data) => { return JSON.parse(data) }]
// 配置 CORS 跨域
// 表示跨域请求时是否需要使用凭证
axios.defaults.withCredentials = true;
axios.defaults.crossDomain = true;
// 设置超时
axios.defaults.timeout = 40000;
// 拦截器的说明
// 1、interceptor必须在请求前设置才有效。
// 2、直接为axios全局对象创建interceptor, 会导致全局的axios发出的请求或接收的响应都会被拦截到, 所以应该使用axios.create() 来创建单独的axios实例。
let instance = axios.create({
baseURL: '',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
withCredentials: true,
})
// Add a request interceptor
instance.interceptors.request.use(function(config) {
// POST 请求参数处理成 axios post 方法所需的格式
if (config.method === 'post' || config.method === "put" || config.method === "delete") {
//config.data = JSON.stringify(config.data);
}
Toast.loading('Loading...', 0, () => {
console.log('Load complete !!!');
});
return config;
}, function(error) {
// Do something with request error
Toast.hide()
return Promise.reject(error);
});
// Add a response interceptor
instance.interceptors.response.use(function(response) {
// Do something with response data
Toast.hide()
return response.data;
}, function(error) {
// Do something with response error
Toast.hide()
return Promise.reject(error);
});
export { instance };
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store/store";
import Route from "./router/";
import registerServiceWorker from "./registerServiceWorker";
import { AppContainer } from "react-hot-loader";
const render = Component => {
ReactDOM.render(
//绑定redux、热加载
// Provider作为顶层组件,提供数据源,然后可以源源不断的从它向下流到各级子孙节点上去,所以Redux把store注册到Provider中
// Provider接受一个属性store,这就是我们全局的数据store。我们要根据reducer函数来创建它
,
document.getElementById("root")
);
};
render(Route);
// Webpack Hot Module Replacement API
if (module.hot) {
module.hot.accept("./router/", () => {
render(Route);
});
}
registerServiceWorker();