React全家桶-05-react-redux的使用

110 阅读3分钟

1.react-redux的使用:

这节这要是来通过react-redux来实现之前juejin.cn/post/694011…登录的重构,来让项目更清晰。先来看下src的目录结构:

src
├─App.js
├─index.js
├─store
|   ├─redux
|   |   ├─index.js
|   |   ├─rootReducer.js
|   |   ├─user                      // 🚀这部分是重点啦~!
|   |   |  ├─actionCreator.js
|   |   |  ├─actionType.js
|   |   |  └reducer.js
├─pages
|   ├─About.js
|   ├─Home.js
|   ├─Login.js
|   ├─NotFound.js
|   └User.js

1.store的创建:

src新建一个store文件夹,如果项目比较大,最好是按照功能进行细分,比如user登录自己是一个文件夹,然后store文件夹下有个index.jsrootReducer.js对所有的功能进行整合。

下面来看下store/index.js中的的代码:

// store/index.js
import {createStore} from "redux";
import {applyMiddleware} from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import {rootReducer} from "./rootReducer";
//创建store
// 🚀 安装完了中间件得通过applyMiddleware去使用中间件
// 🚀 通过combineReducers来组合多个reducer
const store = createStore(rootReducer,applyMiddleware(logger,thunk));
export default store;

这里是将rootReducer引入进来创建store,而且通过applyMiddleware中间件。

2.user模块登录功能的部分

user文件夹中有3个文件:

  • actionType.js :用来声明aciton类型的常量
  • actionCreator.js :封装了一些返回action的函数
  • reducer.js :处理action纯函数

🚀 然后来看下各自部分的代码:

//actionType.js
export const LOGIN = "login";
//actionCreator.js
import {LOGIN} from "./actionType";
export const loginUser = () => {
    return {
        type:LOGIN
    }
}
//🚀 redux-thunk可以让dispatch派发一个函数,这个函数接收dispatch为参数
export const login =  (dispatch)=>{
    setTimeout(()=>{
        const action = loginUser();
        dispatch(action);
    },1000)
};
//reducer.js
import {LOGIN} from "./actionType";
import {login} from "./actionCreator";
// 初始状态
const initState = {
    isLogin:false,
    userInfo:{}
}
// reducer是纯函数
function user(state = initState,action){
        switch (action.type){
            case LOGIN:
                return {isLogin:true}
            default:
                return initState
        }
}
export default user;
// 🚀 字面意思就是将redux中的state,匹配到组件的this.props中
export const mapStateToProps = state => {
    return {
        // 🚀 加上当前状态的key,来进行模块化的标识
        user:state.user
    }
}
// 🚀从mapDispatch的字面意思去理解就是将dispatch匹配到组件的this.props中去
export const mapDispatchToProps = dispatch =>{
    return {
        login(){
            dispatch(login)
        }
    }
}
  • mapStateToProps:字面意思就是将redux中的state,匹配到组件的this.props
  • mapDispatchToProps:从mapDispatch的字面意思去理解就是将dispatch匹配到组件的this.props中去 然后是rootReducer.js对模块的各个子reducer进行合并的代码:
// store/rootReducer.js
import {combineReducers} from "redux";
import userReducer from "./user/reducer";

export const rootReducer  = combineReducers({
    user:userReducer
})

3.项目根目录的index.js

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import store from "./store/redux"
import {Provider} from "react-redux";
import App from "./App";
function render(){
    ReactDOM.render(
        <Provider store = {store}>
            <App/>
        </Provider>
        ,
        document.getElementById('root')
    );
}
render();
// 🚀注册 订阅 每次state更新时,就重新的render
stores.subscribe(render)
  • store引入,然后通过react-redux中的Privider,把store作为props传出去,并对App组件进行包裹。
  • 通过store.subscribe对页面进行订阅

4.App.js组件:

import React, {Component} from 'react';
import {BrowserRouter as Router,Route,Link,Switch,Redirect} from "react-router-dom";
import Home from "./pages/Home.js";
import NotFound from "./pages/NotFound";
import About from "./pages/About";
import Login from "./pages/Login";
import {connect} from "react-redux";
import {mapStateToProps} from "./store/redux/user/reducer";

@connect(mapStateToProps)
class PrivateRoute extends Component{
    render(){
        const Comp = this.props.sendComponent;
        console.log("我是组件comp",Comp);
        return(
            <Route
                {...this.props}
                // 🚀 这里最好是用component而不是render
                component={
                    (props) =>
                        this.props.user.isLogin ?
                            (<Comp {...props} />) :
                            (<Redirect to={{ pathname: '/login',
                                state: { from: props.location } }} />)
                }>
            </Route>
        )
    }
}


class App extends Component {
    render() {
    return (
          <Router>
            <ul>
              <li>
                <Link to="/"> 首页 </Link>
              </li>
                <li>
                    <Link to="/about"> 关于我 </Link>
                </li>
            </ul>
            {/*路由配置*/}
              <Switch>
                {/*🚀①号*/}
                <Route exact path="/home" component={Home} />
                <Route  path="/user" component={User}/>
                {/* 🚀PrivateRoute是个高阶组件 */}
                <PrivateRoute  path="/about" sendComponent={About}/>
                <Route path="/login" component={Login}/>
                {/* 🚀 这里要放在NotFound前面,因为Switch找到了符合条件的组件,就不往下面继续走了*/}
                <Redirect to="/home"/>
                <Route component={NotFound}/>
              </Switch>
          </Router>
    );
  }
}
export default App;
  • PrivateRoute是对路由Route的封装
  • <PrivateRoute path="/about" sendComponent={About}/>挂在sendComponent上的About组件在PrivateRoute中是通过this.props.sendComponent获得的。
  • 通过mapStateToProps拿到的this.props.user.isLogin来判断渲染哪个

5.最后来看下Login.js组件:

//page/Login.js
import React, {Component} from 'react';
import {Redirect} from "react-router-dom";
import {connect} from "react-redux";
import {mapStateToProps,mapDispatchToProps} from "../store/redux/user/reducer";

@connect(mapStateToProps,mapDispatchToProps)
class Login extends Component {
    toOrigin = ()=>{
        // 🚀 指状态的时候需要使用前面的key,也就是user,但是函数的时候不用加,直接this.props.login就可以了
        this.props.login();
    }
    render() {
        console.log("PrivateRoute传递过来的值",this.props.location.state);
        let path = this.props.location.state.from.pathname;
        let {isLogin} = this.props.user;
        if(isLogin){
            return <Redirect to={path}/>
        }
        else{
            return (
                <div>
                    <p>请先登录</p>
                    <button onClick={this.toOrigin}>登 录</button>
                </div>
            );
        }
    }
}
export default Login;
  • 通过@connect(mapStateToProps,mapDispatchToProps)装饰器装饰之后,toOrigin函数中的this.props.login()其实调用的是在reducer.js中定义的mapDispatchToProps中的login函数