1.安装依赖
cnpm i redux react-redux redux-thunk redux-devtools-extension --save
2.创建仓库 【store/index.js】
import {createStore,applyMiddleware,combineReducers} from "redux"
import thunk from "redux-thunk"
import {composeWithDevTools} from "redux-devtools-extension"
import test from "./modules/test"
let reducer=combineReducers({
test
})
let store=createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
export default store
3.通过Provider关联store和App 【src/index.js】
// 10.store ******
import store from "./store"
import { Provider } from "react-redux"
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
,
document.getElementById('root')
);
4.注册模块
1.创建一个register.js [store/register.js]
//初始值
const initState={}
//types
const TYPES={
}
//修改state的reducer
const reducer=(state=initState,action)=>{
switch(action.type){
default:
return state
}
}
//actionCreators
export const actions={
}
//导出数据
//导出reducer
export default reducer
在【store/index.js】引入 放到根reducer中
import register from "./modules/register"
let reducer=combineReducers({
test,
register
})
2.设计state [register.js]
//初始值
const initState = {
//1.初始化user
user: {
phone: "",
nickname: "",
password: ""
}
}
3.设计actionTypes [register.js]
//types
const TYPES = {
// 2.修改user
REGISTER_CHANGE_USER: "REGISTER_CHANGE_USER"
}
4.设计reducer[register.js]
//修改state的reducer
const reducer = (state = initState, action) => {
switch (action.type) {
// 3.修改user的reducer
case TYPES.REGISTER_CHANGE_USER:
//action={type:TYPES.REGISTER_CHANGE_USER,key:"phone",value:"123"}
return {
...state,
user: {
...state.user,
[action.key]: action.value
}
}
break
default:
return state
}
}
5.设计actionCreator [register.js]
//actionCreators
export const actions = {
// 4.修改user的action
changeUser: (key, value) => ({
type: TYPES.REGISTER_CHANGE_USER,
key: key,
value: value
}),
}
6.设计selector [register.js]
//导出数据
export const getUser = state => state.register.user;
7.关联组件Register.jsx
const mapStateToProps = (state) => {
return {
user: getUser(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
register: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Test)
render() {
let { user, register: { changeUser }} = this.props
return (
<div>
<Header title="注册" back></Header>
<div>user:{JSON.stringify(user)}</div>
<List.Item prefix='账号:'>
<Input placeholder='请输入账号' clearable onChange={v => changeUser( 'phone',v)} />
</List.Item>
<List.Item prefix='昵称:'>
<Input placeholder='请输入昵称' clearable onChange={v => changeUser( 'nickname',v)} />
</List.Item>
<List.Item prefix='密码:'>
<Input placeholder='请输入密码' clearable onChange={v => changeUser('password',v)} />
</List.Item>
<div className='text-center'>
<Button color='primary'>注册</Button>
</div>
</div>
)
}
8.用户点了注册按钮,要发请求
设计actionCreator 【register.js】
//actionCreators
export const actions = {
// 4.修改user的action
changeUser: (key, value) => ({
type: TYPES.REGISTER_CHANGE_USER,
key: key,
value: value
}),
//注册请求
reqRegister: (push) => {
return (dispatch, getState) => {
let {
register: {
user
}
} = getState()
//发注册的请求
reqregister(user).then(res => {
if (res.data.code == 200) {
Toast.show({
content: res.data.msg,
})
push("/login")
}
})
}
}
}
关联组件【register.jsx】
let { user, register: { changeUser,reqRegister } ,history:{push}} = this.props
<Button color='primary' onClick={() =>reqRegister(push)}>注册</Button>
5.登录模块
1.创建一个login.js [store/login.js]
//初始值
const initState={}
//types
const TYPES={
}
//修改state的reducer
const reducer=(state=initState,action)=>{
switch(action.type){
default:
return state
}
}
//actionCreators
export const actions={
}
//导出数据
//导出reducer
export default reducer
在【store/index.js】引入 放到根reducer中
import login from "./modules/login"
let reducer=combineReducers({
test,
register,
login,
})
2.设计state [login.js]
//初始值
const initState = {
// 1.1 初始化user
user: {
phone: "",
password: ""
},
// 2.1 用户信息
userInfo: sessionStorage.getItem("userInfo")?JSON.parse(sessionStorage.getItem("userInfo")):{}
}
3.设计actionTypes [login.js]
//types
const TYPES = {
// 1.2 修改user 的type
LOGIN_CHANGE_USER: "LOGIN_CHANGE_USER",
// 2.2 修改userInfo
LOGIN_CHANGE_USERINFO: "LOGIN_CHANGE_USERINFO"
}
4.设计reducer[login.js]
//修改state的reducer
const reducer = (state = initState, action) => {
switch (action.type) {
// 1.3 修改user 的reducer
case TYPES.LOGIN_CHANGE_USER:
//action={type:TYPES.LOGIN_CHANGE_USER,key:"phone",value:"12"}
return {
...state,
user: {
...state.user,
[action.key]: action.value
}
}
break;
// 2.3 userInfo reducer
case TYPES.LOGIN_CHANGE_USERINFO:
//action={type:TYPES.LOGIN_CHANGE_USERINFO,userInfo:{token:"11",uid:'11'}}
return {
...state,
userInfo: action.userInfo
}
break;
default:
return state
}
}
5.设计actionCreator [login.js]
//actionCreators
export const actions = {
// 1.4 修改user 的action
changeUser: (key, value) => ({
type: TYPES.LOGIN_CHANGE_USER,
key,
value
}),
//1.5点击了登录的逻辑
reqLogin: (push) => {
return (dispatch, getState) => {
let {
login: {
user
}
} = getState()
//登录的请求
reqlogin(user).then(res => {
if (res.data.code === 200) {
//1.5.1 弹成功
Toast.show({
content: res.data.msg,
})
// 1.5.2 存用户信息 res.data.list
dispatch(actions.changeUserInfo(res.data.list))
// 1.5.3 跳转页面
push("/index/home")
}
})
}
},
//2.4 修改userInfo
changeUserInfo: (userInfo) => {
//同步到本地存储
if (userInfo.token) {
sessionStorage.setItem("userInfo", JSON.stringify(userInfo))
} else {
sessionStorage.removeItem("userInfo")
}
return {
type: TYPES.LOGIN_CHANGE_USERINFO,
userInfo
}
}
}
6.设计selector [login.js]
// 1.5 导出user
export const getUser = state => state.login.user;
// 2.5到处userInfo
export const getUserInfo = state => state.login.userInfo;
7.关联组件Login.jsx
const mapStateToProps = (state) => {
return {
user: getUser(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
login:bindActionCreators(actions,dispatch)
}
}
class Login extends Component {
render() {
let {user,login:{changeUser,reqLogin},history:{push}}=this.props
return (
<div>
<Header title="登录" register></Header>
<div>user:{JSON.stringify(user)}</div>
<List.Item prefix='账号:'>
<Input placeholder='请输入账号' clearable onChange={v => changeUser('phone',v)} />
</List.Item>
<List.Item prefix='密码:'>
<Input placeholder='请输入密码' clearable onChange={v => changeUser( 'password',v)} />
</List.Item>
<Button color='primary' onClick={()=>reqLogin(push)} >登录</Button>
</div>
)
}
}
6.首页模块
1.创建一个home.js [store/home.js]
//初始值
const initState={}
//types
const TYPES={
}
//修改state的reducer
const reducer=(state=initState,action)=>{
switch(action.type){
default:
return state
}
}
//actionCreators
export const actions={
}
//导出数据
//导出reducer
export default reducer
在【store/index.js】引入 放到根reducer中
import home from "./modules/home"
let reducer=combineReducers({
test,
register,
login,
home
})
2.设计state [home.js]
//初始值
const initState = {
//1.1 分类
cates: [],
// 2.1 商品列表
goods: [],
//3.1 导航数据
navs: ['热卖', '新品', '推荐'],
// 4.1 n
n:0
}
3.设计actionTypes [home.js]
//types
const TYPES = {
// 1.2 分类type
HOME_CHANGE_CATES: "HOME_CHANGE_CATES",
// 2.2 goods type
HOME_CHANGE_GOODS: "HOME_CHANGE_GOODS",
// 4.2 n type
HOME_CHANGE_N:"HOME_CHANGE_N"
}
4.设计reducer[home.js]
//修改state的reducer
const reducer = (state = initState, action) => {
switch (action.type) {
// 1.3 cates reducer
case TYPES.HOME_CHANGE_CATES:
//action={type:TYPES.HOME_CHANGE_CATES,cates:[1,2,3]}
return {
...state,
cates: action.cates
}
break;
// 2.3 goods reducer
case TYPES.HOME_CHANGE_GOODS:
//action={type:TYPES.HOME_CHANGE_GOODS,goods:[1,2,3]}
return {
...state,
goods: action.goods
}
break;
// 4.3 n reducer
case TYPES.HOME_CHANGE_N:
//action={type:TYPES.HOME_CHANGE_N,n:1}
return {
...state,
n:action.n
}
default:
return state
}
}
5.设计actionCreator [home.js]
//actionCreators
export const actions = {
// 1.4 cates action
changeCates: (cates) => ({
type: TYPES.HOME_CHANGE_CATES,
cates
}),
//1.6 请求cates
reqCates: () => (dispatch,getState) => {
// 如果分类数据已经有了,就return ;
let {home:{cates}}=getState()
if(cates.length>0){
return;
}
reqgetcate().then(res => {
if (res.data.code == 200) {
//修改cates
dispatch(actions.changeCates(res.data.list))
}
})
},
// 2.4 goods action
changeGoods: (goods) => ({
type: TYPES.HOME_CHANGE_GOODS,
goods
}),
// 2.6 请求goods
reqGoods: () => (dispatch,getState) => {
// 状态层作为缓存层
// 如果商品数据已经有了,就return ;
let {home:{goods}}=getState()
if(goods.length>0){
return;
}
//请求goods
reqgethortgoods().then(res => {
if (res.data.code == 200) {
//修改goods
dispatch(actions.changeGoods(res.data.list))
}
})
},
// 4.4 修改n
changeN:n=>({type:TYPES.HOME_CHANGE_N,n})
}
6.设计selector [home.js]
需要先安装reselect
npm i reselect --save
//1.5 导出cates
export const getCates = state => state.home.cates;
//2.5 导出goods
export const getGoods = state => state.home.goods;
//3.2 导出navs
export const getNavs = state => state.home.navs;
// 4.5导出 n
export const getN = state => state.home.n;
// 2.7 导出展示的列表数据
// export const getShowGoods = state => {
// let goods = state.home.goods;
// let n=state.home.n;
// return goods.length > 0 ? goods[n].content : []
// }
export const getShowGoods=createSelector([getGoods,getN],(goods,n)=>{
return goods.length > 0 ? goods[n].content : []
})
7.关联组件Home.jsx
const mapStateToProps = (state) => {
return {
cates: getCates(state),
navs: getNavs(state),
showGoods: getShowGoods(state),
n: getN(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
home: bindActionCreators(actions, dispatch)
}
}
componentDidMount() {
this.props.home.reqCates()
this.props.home.reqGoods()
}
render() {
let { cates, history: { push }, navs, showGoods, home: { changeN }, n } = this.props
return (
<div>
<Header title="首页"></Header>
{/* 快速导航 */}
<Tabs defaultActiveKey='1' onChange={(e) => push('/list?id=' + e)}>
{
cates.map(item => {
return (
<Tabs.Tab key={item.id} title={item.catename} >
</Tabs.Tab>
)
})
}
</Tabs>
{/* 商品列表 */}
<List navs={navs} showGoods={showGoods} changeN={changeN} n={n}></List>
</div>
)
}
【home/components/list.jsx】
import React from 'react'
import {Link} from "react-router-dom"
export default function List(props) {
let {navs,showGoods,changeN,n}=props
return (
<div>
{/* 导航 */}
<div className='nav'>
{
navs.map((item,index)=>(
<span key={item} className={index==n?'active':''} onClick={()=>changeN(index)}>{item}</span>
))
}
</div>
{/* 遍历列表 */}
{
showGoods.map(item=>(
<div key={item.id}>
<Link to={"/detail/"+item.id}>{item.goodsname}</Link>
</div>
))
}
</div>
)
}
7.分类列表页模块
1.创建一个list.js [store/list.js]
//初始值
const initState={}
//types
const TYPES={
}
//修改state的reducer
const reducer=(state=initState,action)=>{
switch(action.type){
default:
return state
}
}
//actionCreators
export const actions={
}
//导出数据
//导出reducer
export default reducer
在【store/index.js】引入 放到根reducer中
import list from "./modules/list"
let reducer=combineReducers({
test,
register,
login,
list
})
2.设计state [list.js]
let oldid=null;
//初始值
const initState = {
goods: [],
}
3.设计actionTypes [list.js]
//types
const TYPES = {
// 2.2 goods type
LIST_CHANGE_GOODS: "LIST_CHANGE_GOODS",
}
4.设计reducer[list.js]
//修改state的reducer
const reducer = (state = initState, action) => {
switch (action.type) {
// 2.3 goods reducer
case TYPES.LIST_CHANGE_GOODS:
//action={type:TYPES.LIST_CHANGE_GOODS,goods:[1,2,3]}
return {
...state,
goods: action.goods
}
break;
default:
return state
}
}
5.设计actionCreator [list.js]
//actionCreators
export const actions = {
// 2.4 goods action
changeGoods: (goods) => ({
type: TYPES.LIST_CHANGE_GOODS,
goods
}),
// 2.6 请求goods
reqGoods: (cateid) => (dispatch, getState) => {
// 如果上一次请求的id和这一次请求的id一样,return
if(oldid==cateid) return;
// 如果不一样,立刻将本次的id存为旧的id
oldid=cateid;
reqgetgoodlist({
cateid,
type:1
}).then(res=>{
if(res.data.code==200){
dispatch(actions.changeGoods(res.data.list.goodData?res.data.list.goodData:[]))
}
})
},
}
6.设计selector [list.js]
//导出数据
//2.5 导出goods
export const getGoods = state => state.list.goods;
7.关联组件List.jsx
const mapStateToProps = (state) => {
return {
goods: getGoods(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
list: bindActionCreators(actions, dispatch)
}
}
componentDidMount() {
let id = qs.parse(this.props.location.search.slice(1)).id
this.props.list.reqGoods(id)
}
render() {
let {goods}=this.props
return (
<div>
<Header title="商品列表" back></Header>
{goods.length == 0 ? <Empty description='暂无数据' /> : null}
{/* 列表 */}
{
goods.map(item => (
<div key={item.id}>
<Link to={"/detail/" + item.id + '/' + item.goodsname}>{item.goodsname}</Link>
</div>
))
}
</div>
)
}
8.数据交互 使用状态层数据和方法【http.js】
import {getUserInfo,actions} from "../store/modules/login"
import store from "../store"
请求拦截
//请求拦截
axios.interceptors.request.use(req => {
if(req.url!=='/api/login'&&req.url!=='/api/register'){
req.headers.authorization=getUserInfo(store.getState()).token;
}
return req;
})
响应拦截
//响应拦截
axios.interceptors.response.use(res => {
// 打印
console.group("本次请求地址:" + res.config.url)
console.log(res)
console.groupEnd()
//统一处理失败
if (res.data.code !== 200) {
Toast.show({
content: res.data.msg,
})
}
//统一处理掉线
if(res.data.msg==='登录已过期或访问权限受限'){
//触发仓库的changeUserInfo ================此处使用状态层========================
store.dispatch(actions.changeUserInfo({}))
//跳转路由
window.open("/login","_self")
}
return res;
})