第七章 redux
五、redux 异步编程
1. 理解
- redux 默认是不能进行异步处理的,
- 某些时候应用中需要在 redux 中执行异步任务(ajax, 定时器)
2. 使用异步中间件
- npm install --save redux-thunk
3. 代码 - 异步 action 版
3.1 store
javascript
复制代码
/* src/redux/store.js */
/**
* 该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore, applyMiddleware } from "redux";
//引入为Count组件服务的reducer
import countReducer from "./count_reducer";
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer, applyMiddleware(thunk));
3.2 count_action
javascript
复制代码
/* src/redux/count_action.js */
/**
* 该文件专门为Count组件生成action对象
*/
import { INCREMENT, DECREMENT } from "./constant";
//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(createIncrementAction(data));
}, time);
};
};
3.3 Count
javascript
复制代码
/* src/components/Count/index.jsx */
import React, { Component } from "react";
//引入store,用于获取redux中保存状态
import store from "../../redux/store";
//引入actionCreator,专门用于创建action对象
import {
createDecrementAction,
createIncrementAction,
createIncrementAsyncAction,
} from "../../redux/count_action";
export default class Count extends Component {
//加法
increment = () => {
const { value } = this.selectNumber;
store.dispatch(createIncrementAction(value * 1));
};
//减法
decrement = () => {
const { value } = this.selectNumber;
store.dispatch(createDecrementAction(value * 1));
};
//奇数加
incrementIfOdd = () => {
const { value } = this.selectNumber;
const count = store.getState();
if (count % 2 !== 0) {
store.dispatch(createIncrementAction(value * 1));
}
};
//异步加
incrementAsync = () => {
const { value } = this.selectNumber;
store.dispatch(createIncrementAsyncAction(value * 1, 500));
};
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
3.4 总结
bash
复制代码
(1).明确:延迟的动作不想交给组件自身,想交给action
(2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
(3).具体编码:
1).yarn add redux-thunk,并配置在store中
2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
3).异步任务有结果后,分发一个同步的action去真正操作数据。
(4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
六、react-redux
1. 理解
- 一个 react 插件库
- 专门用来简化 react 应用中使用 redux
2. react-Redux 将所有组件分成两大类
2.1 UI 组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过 props 接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在 components 文件夹下
2.2 容器组件
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 使用 Redux 的 API
- 一般保存在 containers 文件夹下
2.3 相关 API
2.3.1 Provider:让所有组件都可以得到 state 数据
javascript
复制代码
<Provider store={store}>
<App />
</Provider>
2.3.2 connect:用于包装 UI 组件生成容器组件
javascript
复制代码
import { connect } from 'react-redux'
connect(
mapStateToprops,
mapDispatchToProps
)(Counter)
2.3.3 mapStateToprops:将外部的数据(即 state 对象)转换为 UI 组件的标签属性
javascript
复制代码
const mapStateToprops = function (state) {
return {
value: state
}
}
2.3.4 mapDispatchToProps:将分发 action 的函数转换为 UI 组件的标签属性
3. 代码 - react-redux 的基本使用
- 安装:npm install react-redux
3.1 index
javascript
复制代码
/* src/index.js */
//引入react核心库
import React from "react";
//引入ReactDOM
import ReactDOM from "react-dom";
//引入App组件
import App from "./App";
import store from "./redux/store";
ReactDOM.render(<App />, document.getElementById("root"));
//监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(() => {
ReactDOM.render(<App />, document.getElementById("root"));
});
3.2 App
javascript
复制代码
/* src/App.jsx */
import React, { Component } from "react";
import Count from "./containers/Count";
import store from "./redux/store";
export default class App extends Component {
render() {
return (
<div>
{/* 给容器组件传递store */}
<Count store={store} />
</div>
);
}
}
3.3 Count(Component)
javascript
复制代码
/* src/components/Count/index.jsx */
import React, { Component } from "react";
export default class Count extends Component {
//加法
increment = () => {
const { value } = this.selectNumber;
this.props.jia(value * 1);
};
//减法
decrement = () => {
const { value } = this.selectNumber;
this.props.jian(value * 1);
};
//奇数加
incrementIfOdd = () => {
const { value } = this.selectNumber;
if (this.props.count % 2 !== 0) {
this.props.jia(value * 1);
}
};
//异步加
incrementAsync = () => {
const { value } = this.selectNumber;
this.props.jiaAsync(value * 1, 500);
};
render() {
return (
<div>
<h1>当前求和为:{this.props.count}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
3.4 Count(Container)
javascript
复制代码
/* src/containers/Count/index.jsx */
//引入Count的UI组件
import CountUI from "../../components/Count";
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction,
} from "../../redux/count_action";
//引入connect用于连接UI组件与redux
import { connect } from "react-redux";
/*
1.mapStateToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapStateToProps用于传递状态
*/
function mapStateToProps(state) {
return { count: state };
}
/*
1.mapDispatchToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch) {
return {
jia: (number) => dispatch(createIncrementAction(number)),
jian: (number) => dispatch(createDecrementAction(number)),
jiaAsync: (number, time) =>
dispatch(createIncrementAsyncAction(number, time)),
};
}
//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
3.5 总结
bash
复制代码
(1).明确两个概念:
1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
2).容器组件:负责和redux通信,将结果交给UI组件。
(2).如何创建一个容器组件------------靠 react-redux 的 connect函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapStateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
(3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
(4).备注2:mapDispatchToProps,也可以是一个对象
4. 代码 - 优化 1:简写 mapDispatch
Count(Container)
javascript
复制代码
/* src/containers/Count/index.jsx */
//引入Count的UI组件
import CountUI from "../../components/Count";
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction,
} from "../../redux/count_action";
//引入connect用于连接UI组件与redux
import { connect } from "react-redux";
//使用connect()()创建并暴露一个Count的容器组件
export default connect((state) => ({ count: state }), {
jia: createIncrementAction,
jian: createDecrementAction,
jiaAsync: createIncrementAsyncAction,
})(CountUI);
5. 代码 - 优化 2:Provider 组件的使用
5.1 index
javascript
复制代码
/* src/index.js */
//引入react核心库
import React from "react";
//引入ReactDOM
import ReactDOM from "react-dom";
//引入App组件
import App from "./App";
import store from "./redux/store";
import { Provider } from "react-redux";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
5.2 App
javascript
复制代码
/* src/App.jsx */
import React, { Component } from "react";
import Count from "./containers/Count";
export default class App extends Component {
render() {
return (
<div>
<Count />
</div>
);
}
}
6. 代码 - 优化 3:整合 UI 组件和容器组件
6.1 Count(Container)
javascript
复制代码
/* src/containers/Count/index.jsx */
import React, { Component } from "react";
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction,
} from "../../redux/count_action";
//引入connect用于连接UI组件与redux
import { connect } from "react-redux";
// 定义UI组件
class Count extends Component {
//加法
increment = () => {
const { value } = this.selectNumber;
this.props.jia(value * 1);
};
//减法
decrement = () => {
const { value } = this.selectNumber;
this.props.jian(value * 1);
};
//奇数加
incrementIfOdd = () => {
const { value } = this.selectNumber;
if (this.props.count % 2 !== 0) {
this.props.jia(value * 1);
}
};
//异步加
incrementAsync = () => {
const { value } = this.selectNumber;
this.props.jiaAsync(value * 1, 500);
};
render() {
return (
<div>
<h1>当前求和为:{this.props.count}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
);
}
}
//使用connect()()创建并暴露一个Count的容器组件
export default connect((state) => ({ count: state }), {
jia: createIncrementAction,
jian: createDecrementAction,
jiaAsync: createIncrementAsyncAction,
})(Count);
6.2 总结
bash
复制代码
(1).容器组件和UI组件整合一个文件
(2).无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。
(3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
(4).mapDispatchToProps也可以简单的写成一个对象
(5).一个组件要和redux"打交道"要经过哪几步?
(1).定义好UI组件---不暴露
(2).引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
(3).在UI组件中通过this.props.xxxxxxx读取和操作状态