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.js
和rootReducer.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函数