🌈个人主页:前端青山
🔥系列专栏:React篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来React篇专栏内容:React-Redux(二)
目录
react-redux
模块化
redux-thunk
react-redux
网址:React Redux | React Redux
React-Redux是Redux的官方针对React开发的扩展库,默认没有在React项目中安装,需要手动来安装。react-redux是依赖于redux,所以必须先安装redux。
我们可以理解为react-redux就是redux给我们提供的高阶组件加工厂。
npm i -S react-redux
React-redux所能解决的问题是:
-
使用它以后我们不需要在每个组件中再去 手动订阅数据的更新了。
-
没有了数据的初始化state赋值,当前组件自身state和这个redux不冲突了
-
使用本节的react-redux与下一节的redux-thunk并不是为了简化代码的,它们存在的意义是解决前面所遇到的问题
使用步骤
-
在项目入口文件中定义Provider
-
该步骤的操作有点类似于之前组件通信中的context那块的操作
-
将整个仓库作为商品提供给App根组件,后续的所有的组件都可以获取到仓库store中的数据
-
注意:与context不一样,这里绑定数据使用的属性是“store”
-
src/index.js文件中的示例代码:
// 导入 import React from "react"; import ReactDOM from "react-dom"; // 导入provider import { Provider } from "react-redux"; import store from "./store/index"; // 导入需要展示的组件 import App from "./Login"; // 渲染视图 // 在展示app组件的时候需要按照组件的形式进行操作 ReactDOM.render( <Provider store={store}> <App></App> </Provider> document.getElementById("root") );
-
-
在需要使用redux的组件中使用
-
这个步骤与vuex中map系列函数(mapState,mapMutations,mapActions、mapGetters)的思想是一样的
-
思想:将仓库中的属性和方法映射成当前组件自身的属性和方法
-
在实际使用的时候组件中不再需要使用store对象了(包括初始的获取数据:store.getState()、store.dispatch()、store.subscribe())
-
步骤
-
在需要使用reudx的组件前面导入react-redux提供的高阶组件:connect
-
编写映射方法(请注意,这个方法映射不是类组件的方法,而是在类组件外写的方法)
-
mapStateToProps(state)
-
作用:将仓库中的state数据源映射成本组件的属性props,返回一个props对象
-
参数:仓库中的state
-
-
mapDispatchToProps(dispatch)
-
作用:将派发action的方法映射成当前组件自身的属性,该方法也要求返回一个对象,该对象中存放的就是派发action的方法集合
-
参数:dispatch如同之前的store.dispatch()
-
-
编写时,可以写箭头函数,也可以写常规函数
-
-
应用高阶组件connect,写法是固定的
-
// 在组件最后导出的时候改写成如下: export default connect(mapStateToProps,mapDispatchToProps)(ComponentName)
-
-
-
组件中实际使用时的参考代码:以jsx为例
import React, { Component } from "react"; // 需要导入store // import store from "../store/index"; // 导入action创建模块(导出里面全部的方法) import * as actionCreate from "../store/actions/index"; // 导入type import { MOD_COUNT, MOD_AGE } from "../store/types/index"; // import * as types from "../store/types/index"; // 第一步:在需要使用redux组件中导入一个由react-redux提供的hoc import { connect } from "react-redux"; class Counter extends Component { // 在constructor中获取store中的数据 constructor(props) { super(props); // 获取store数据(一次性,不具备响应式) // this.state = store.getState(); // 订阅数据的更新 // store.subscribe(() => this.setState(() => store.getState())); } render() { console.log(this.state); return ( <div> <div>当前Store中的数据是:{this.props.tool.count}</div> <button onClick={this.props.addCount}>点击+9</button> <hr /> <div>当前Store中的数据是:{this.props.user.age}</div> <button onClick={this.props.addAge}>点击+1</button> </div> ); } } // 第二步:在类外面定义俩个映射方法 // 将redux中的state数据源映射到本组件自身的props中 function mapStateToProps(state) { // return state.user; // return state.tool; return state; } // 将dispatch映射成自身组件的props function mapDispatchToProps(dispatch) { // 该方法返回一个对象,对象中都是方法 return { addCount() { dispatch(actionCreate.createAction(MOD_COUNT, 9)); }, addAge() { dispatch(actionCreate.createAction(MOD_AGE, 1)); }, }; } // 第三步:应用HOC // connect函数的俩个参数顺序不能颠倒 export default connect(mapStateToProps, mapDispatchToProps)(Counter);
-
模块化
如果redux需要使用多个模块,请使用combineReducers方法将多个reudcer函数进行组合,得到一个根reducer对象,将对象传递给创建仓库的方法:Redux Fundamentals, Part 3: State, Actions, and Reducers | Redux
针对redux的模块化,在一个常规项目中会将其代码拆分成以下几个部分:
-
Reducers,建立同名目录,存放模块化之后的reducer
-
Actions:建立同名目录,存放模块化之后的action
-
Type(可选):建立同名目录,存放独立的type声明
具体实现,以项目的代码为准。
面试题:如果项目有100甚至更多个reducer模块,后续就得import100次甚至更多次,虽然代码简单,但是很繁琐,请问如何优化?
// 代码优化,批量导入
const files = require.context("./reducers", false, /\.js$/);
// 比较固定的处理代码
let members = {}; // 组合成员用的
files.keys().forEach((element) => {
// element是对应的模块文件的路径
console.log(element);
// 依据路径获取导出的成员
let member = files(element).default;
// 获取文件名充当对象的属性名
let filename = element.replace(/(.*\/)*([^.]+).*/gi, "$2");
// 组合成员
members[filename] = member;
});
console.log(members);
redux-thunk
通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰,使用中间件了之后,可以通过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用,同时actionCreator还集成了异步操作中不同的action派发机制,减少编码过程中的代码量。
常见的异步库:
-
Redux-thunk
-
Redux-saga
-
Redux-effects
-
Redux-side-effects
-
Redux-loop
-
Redux-observable
-
…
基于Promise的异步库:
-
Redux-promise
-
Redux-promises
-
Redux-simple-promise
-
Redux-promise-middleware
-
…
这里我们使用一个Redux官方出品的中间件库:redux-thunk
在使用前需要先安装这个中间件:
npm i -S redux-thunk
步骤:
-
在仓库的创建文件
store/index.js
文件中导入中间件的应用方法,再去导入redux-thunk,并且应用-
导入redux提供的中间件使用的方法:applyMiddleware
-
-
会产生报错(浏览器的redux调试工具的报错)需要解决
-
解决思路:查看
[插件的项目主页] https://github.com/zalmoxisus/redux-devtools-extension#usage
,找解决办法
修改为的配置如下:
// 解决插件报错的操作 const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore( // 合并多个reducer(整合数据源),不合并会报错 combineReducers({ counter, global }), // 应用中间件 composeEnhancers(applyMiddleware(thunk)) // 必须要加上一段插件的配置工具,才能在浏览器中使用redux扩展 // window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() );
-
-
去需要做异步处理的action的位置去使用异步实现(通过dispatch派发action)
-
// - 异步方法(载荷可能是异步获取的数据) export const createActionAsync = (type, payload) => { // 异步代码先不写(暂时没有异步中间件) // return { type, payload }; // setTimeout(() => { // return { type, payload }; // }, 1000); // 异步写法 return (dispatch) => { setTimeout(() => { dispatch({ type, payload }); }, 3000); }; };
-