React app笔记

62 阅读5分钟

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) => ({
         typeTYPES.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 {
        usergetUser(state)
    }
}
const mapDispatchToProps = (dispatch) => {
    return {
        registerbindActionCreators(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) => ({
        typeTYPES.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) => ({
        typeTYPES.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 {
            typeTYPES.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 {
        usergetUser(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) => ({
        typeTYPES.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) => ({
        typeTYPES.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 {Linkfrom "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) => ({
        typeTYPES.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 {
        goodsgetGoods(state)
    }
}
const mapDispatchToProps = (dispatch) => {
    return {
        listbindActionCreators(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;
})