项目结构 create-react-app创建的
先说redux
defaultState.js
默认的state
export default {
hasLogin: false, //是否登录的标记
userInfo: null,
memu: [1, 2, 3],// 用于页面权限,后端返回
authKey: '',
sessionId: '',
contractList: [], // 合同类型
defaultSelectedKeys: ['1'], // 左边导航栏默认选中,
}
action.js
返回一个对象,触发reducer
import $api from '@/api'
export const getContractList = () => {
return async dispatch => { // 异步的action
try {
let res = await $api.post('/common/service_list', {
action_id: '0c0a2b9cd16e9594774c2979951d709b4',
pid: '79ff0e6ef55fc2e14cfdc4b81193de6f70'
})
if (res.code === 200) {
dispatch({
type: 'setContractList',
data: res.data
})
}
} catch (error) {
console.error(error)
}
}
}
export const setUserInfo = (payload) => {
return {
type: "setUserInfo",
data: payload
}
}
// 设置左边导航默认值
export const setDefaultSelectedKeys = (val) => {
return {
type: "setDefaultSelectedKeys",
data: val
}
}
reducer.js
处理action返回新的state
import defaultState from './defaultState'
export const initData = (state = defaultState, action) => {
switch (action.type) {
case 'setContractList':
return { ...state, contractList: action.data };
case 'setUserInfo':
let { userInfo, memu, authKey, sessionId } = action.data
return { ...state, userInfo, memu, authKey, sessionId, hasLogin: true }
case 'setDefaultSelectedKeys':
return { ...state, defaultSelectedKeys: action.data };
default: return state;
}
}
store的构建
用到两个插件 一个是用于在action里面支持异步的,一个是持久化数据的(利用的localstorage/sessionSt0rage),类似于vue的 vuex-persistedstate
import { createStore, combineReducers, applyMiddleware } from 'redux';
import * as initData from './reducer';
import thunk from 'redux-thunk'; // 用来在action里面支持异步
import { persistStore, persistReducer } from 'redux-persist' // 用来避免刷新导致store重置
import storage from 'redux-persist/lib/storage';
let rootReducer = combineReducers({ ...initData })
const myReducer = persistReducer({
key: 'root',
storage
}, rootReducer);
let store = createStore(
myReducer,
applyMiddleware(thunk)
);
export const persistor = persistStore(store);
export default store;
下面就是把store挂载在react的组件上了,
主要利用react-redux插件
项目的主入口
它提供一个核心组件Provider (把store加载到react的DOM中)
和一个核心方法 connect (把state 和 action映射到组件的props)
import React from 'react';
import ReactDOM from 'react-dom';
import Route from './router/';
import * as serviceWorker from './serviceWorker';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import store, { persistor } from '@/store/store';
import 'antd/dist/antd.css'
import '@/iconfont/iconfont.css'
import { PersistGate } from 'redux-persist/integration/react';
import zhCN from 'antd/es/locale-provider/zh_CN';
import { LocaleProvider } from 'antd'// antd格式化为中文,默认英文?鄙视!
const render = Component => {
ReactDOM.render(
//绑定redux、热加载
<Provider store={store}>
<AppContainer>
<PersistGate loading={null} persistor={persistor}>
<LocaleProvider locale={zhCN}>
<Component />
</LocaleProvider>
</PersistGate>
</AppContainer>
</Provider>,
document.getElementById('root'),
)
}
render(Route);
// Webpack Hot Module Replacement API
if (module.hot) {
module.hot.accept('./router/', () => {
render(Route);
})
}
serviceWorker.unregister();
页面上的应用实例
import React, { Component } from 'react';
import { connect } from 'react-redux';
// import PropTypes from 'prop-types'; // 这个插件可以指定props的类型、是否必穿
import { getContractList, setUserInfo } from '@/store/action'
import { Icon, Input, Button } from 'antd';
import './login.scss'
import $api from '@/api'
class Login extends Component {
static propTypes = {
// orderStatus: PropTypes.object.isRequired,
}
state = {
codeImg: process.env.REACT_APP_ROOT + '/upload/captcha?t=' + Math.random(),
login_name: '',
password: '',
verifyCode: ""
}
changeCode = () => {
this.setState({
codeImg: process.env.REACT_APP_ROOT + '/upload/captcha?t=' + Math.random()
})
}
login = () => {
let params = {
login_name: this.state.login_name,
password: this.state.password,
verifyCode: this.state.verifyCode,
action_id: '0c0a2b9cd16e9594774c2979951d709b4'
}
$api.post('/login/index', params).then(res => {
if (res.code === 200) {
this.props.setUserInfo(res.data)
this.props.getContractList()
// 跳转到首页
this.props.history.push('/home')
}
})
}
componentDidMount () {
//
}
render () {
return (
<div className="loginbox">
<Input
value={this.state.login_name}
type='text'
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
onChange={(e) => {
this.setState({
login_name: e.target.value
})
}}
placeholder="Username"
/>
<Input
value={this.state.password}
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="密码"
onChange={(e) => {
this.setState({
password: e.target.value
})
}}
/>
<Input
value={this.state.verifyCode}
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
onChange={(e) => {
this.setState({
verifyCode: e.target.value
})
}}
placeholder="验证码"
/>
<img src={this.state.codeImg} alt="" onClick={this.changeCode} />
<Button type="primary" onClick={this.login} className="login-form-button" style={{ marginTop: '15px' }}>
login
</Button>
<Button type="link" onClick={() => { this.props.history.push('/home') }} style={{ marginTop: '15px' }}>直接进去</Button>
</div>
);
}
}
export default connect(state => {
return { // 第一个参数是返回store的state
initData: state.initData,
}
},
{ // 这里是action,需要引入
getContractList,
setUserInfo
})(Login); // Login为react的一个页面
页面里面通过this.props访问
这非页面文件读取state
比喻:xhr里面添加请求头,store.getState()即可
import axios from 'axios'
import qs from 'qs'
import store from '@/store/store';
// 配置根路径开发、线上等环境服务端的配置
let root = process.env.REACT_APP_ROOT
axios.defaults.baseURL = root
axios.defaults.withCredentials = true // 跨域
axios.defaults.timeout = 50000
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'
// 请求拦截
axios.interceptors.request.use(
// 请求头添加必要的参数
config => {
let initData = store.getState().initData
config.headers.authKey = initData.authKey
config.headers.sessionId = initData.sessionId
return config
},
error => {
console.error('请求拦截捕获到错误', error)
}
)
// 响应拦截
axios.interceptors.response.use(
response => {
const res = response.data
if (res.code === 200) {
return response
} else {
if (res.code === 101) {
} else if (res.code === 600) {
} else {
console.error(res.error)
}
return Promise.reject(response.data)
}
},
error => {
return Promise.reject(error)
}
)
export default {
get: (urlName = '', params = {}, config = {}) => {
return axios.get(urlName, { params, ...config }).then((res) => {
return res.data
}).catch((error) => {
return error
})
},
// 一般的post
post: (urlName = '', params = {}, config = {}) => {
return axios.post(urlName, qs.stringify(params), config).then((res) => {
return res.data
}).catch((error) => {
return error
})
},
// 直接传json格式的post
jsonPost: (urlName = '', params = {}, ) => {
return axios.post(urlName, params, {
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
return res.data
}).catch((error) => {
return error
})
}
}