項目結構 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
})
}
}