React之react-redux的基本使用
redux 需要我们手动在 componentDidMount 生命周期中订阅事件来更新 state,那么有没有一款工具能自己监听 state 的变化并自动更新呢?答案当然是有的,那就是 React-Redux,它是 React 官方推出的 redux 绑定库,会自己监听 state 的变化并进行更新。
# react-redux 的基本使用
1、安装 react-redux
npm install react-redux
2、创建 reducer
// src/store/countReducer.js文件
const initialState = {
count: 0
}
const countReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD':
// 注意,一定要返回一个新对象,否则redux察觉不到数据变化
return {
...state,
count: state.count + action.data
}
case 'MINUS':
return {
...state,
count: state.count - action.data
}
default:
return state
}
}
export default countReducer
// 使用createSlice的用法,需要前需要先安装@reduxjs/toolkit => npm install @reduxjs/toolkit
// import { createSlice } from "@reduxjs/toolkit";
// const countSlice = createSlice({
// name: "count",
// initialState: { // 初始值须为对象或数组,否则更改时会报错
// count: 0
// },
// reducers: {
// increment: (state, action) => {
// state.count += action.payload; // 这里必须是payload字段,不能换
// },
// decrement: (state, action) => {
// state.count -= action.payload;
// },
// incrementByAmount: (state, action) => {
// state.count += action.payload;
// },
// }
// })
// export const { increment, decrement, incrementByAmount } = countSlice.actions;
// export default countSlice.reducer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
3、创建 Redux Store
// /src/store/index.js
import { createStore, combineReducers } from "redux"
import countReducer from "./count/countReducer";
// import userReducer from "./userReducer";
// const reducer = combineReducers({
// countReducer,
// userReducer
// })
export default createStore(countReducer)
2
3
4
5
6
7
8
9
10
11
4、为 React 提供 Redux Store
在 src/index.js 中引入我们刚刚创建的 store , 通过 React-Redux 的 Provider 组件将 App 组件 包裹起来,并将 store 作为 prop 传入
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from "./store";
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
5、创建 Count 组件
import React from 'react';
import { useSelector, useDispatch } from "react-redux";
// 这是使用createSlice方法创建reducer,需要如下引入,如果是createStore方式,则无需引入
import { increment, decrement, incrementByAmount } from "../../store/countReducer";
const CountRedux = () => {
const count = useSelector(state => state.countReducer.count); // 读取store中的数据
const dispatch = useDispatch(); // 用于dispatch actions
const handleAdd = (data) => {
// createSlice方法
dispatch(increment(data));
// 使用createAction方法
// dispatch({
// type: "ADD",
// data
// });
}
const handleAddAmount = (data) => {
dispatch(incrementByAmount(data));
}
const handleReduce = (data) => {
dispatch(decrement(data));
}
return (
<div>
<p>当前结果:{count}</p>
<button onClick={() => handleAdd(5)}>加</button>
<button onClick={() => handleAddAmount(10)}>加10</button>
<button onClick={() => handleReduce(5)}>减</button>
</div>
);
};
export default CountRedux;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
上面虽然完成了 react-redux 实现加法的案例,但是我们用的仍然是之前 redux 的思路去实现,并没有真正用到 react-redux 的精髓,在 react-redux 中,其实是有这样两个概念的,即 UI 组件和容器组件,通过 connect 方法将容器组件与 UI 组件连接起来,然后在 UI 组件中就可以通过 props 来访问状态和操作状态的方法了。
# react-redux 的完整使用:
# 1、创建 reducer
// /src/redux/countReducer.js
/*
Reducer不应该更改原有 state,应该始终返回新的对象,否则,React Redux 觉察不到数据变化。
*/
const initialState = {
count: 0
}
const countReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD':
// 注意,一定要返回一个新对象,否则redux察觉不到数据变化
return {
...state,
count: state.count + 1
}
case 'MINUS':
return {
...state,
count: state.count - 1
}
default:
return state
}
}
export default countReducer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 2、创建 store
// /src/redux/store.js
/* -------- 老版本用法 --------- */
// 有多个reducer
import { createStore, combineReducers } from "redux"
import countReducer from "./countReducer";
import userReducer from "./userReducer";
const reducer = combineReducers({
countReducer,
userReducer
})
export default createStore(reducer)
// 单个reducer
import { createStore } from "redux"
import countReducer from "./countReducer";
export default createStore(countReducer)
/* --------- 新版本用法 --------- */
/*
当我们在项目中使用createStore来创建store时,编辑器会有一个删除线,
表示这个api是已经废弃的api,并且推荐我们使用configureStore,
因此,我们需要安装Redux Toolkit, npm install @reduxjs/toolkit
它是Redux官方团队提供的一个工具包,提供了一些实用的工具和函数,以便轻松地编写和组织Redux代码。
常用的工具函数:
1、createSlice函数:用于创建Redux的slice(片段),
包括定义初始状态、定义reducer函数以及生成action creators等功能。
2、configureStore函数:用于创建Redux的store对象,并自动集成了一些中间件和插件,
包括Redux DevTools插件和redux-thunk中间件。它简化了Redux store的配置和初始化过程。
*/
import { configureStore } from '@reduxjs/toolkit';
import countReducer from './countReducer';
const store = configureStore({
reducer: {
countReducer
}
})
export default store
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 3、在 /src/index.js 中引入刚创建的 store,并使用 Provider 包裹 App 组件
import React, {StrictMode} from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from "./redux/store";
import { Provider } from 'react-redux'
ReactDOM.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4、在需要使用 redux 的组件中使用
import { Component } from "react";
import { connect } from "react-redux";
class Count extends Component {
render() {
return (
<div>
<h3>当前结果:{this.props.count}</h3>
<button onClick={ () => this.props.add() }>加1</button>
<button onClick={ () => this.props.minus() }>减1</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return state.countReducer // 多个reduder
// return state // 单个reducer
}
const mapDispatchToProps = (dispatch) => {
return {
add: () => {
dispatch({type: "ADD"})
},
minus: () => {
dispatch({type: "MINUS"})
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Count);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
如果将创建 action 的部分抽出来单独为一个文件,如 countActions.js
export function incrementAction(data) {
return {
type: "ADD",
data
}
}
export function decrementAction(data) {
return {
type: "MINUS",
data
}
}
2
3
4
5
6
7
8
9
10
11
12
13
那么上面使用 redux 的组件就可以这样写:
import React from 'react';
import { connect } from "react-redux"
import { incrementAction, decrementAction } from "../../reactRedux/countActions"
const CountRedux = (props) => {
return (
<div>
<p>当前结果:{props.count}</p>
<button onClick={() => props.handleAdd(5)}>加5</button>
<button onClick={() => props.handleReduce(10)}>减10</button>
</div>
);
};
/*
1、mapStateToProps函数返回的是一个对象
2、返回的对象中的key作为传递给UI组件props的key,value作为传递给UI组件props的value
3、mapStateToProps用于传递状态
*/
const mapStateToProps = (state) => {
return state.countReducer
}
/*
1、matDispatchToProps函数返回的是一个对象
2、返回的对象中的key作为传递给UI组件props的key,value作为传递给UI组件props的value
3、mapDispatchToProps用于传递操作状态的方法
*/
const mapDispatchToProps = (dispatch) => {
return {
handleAdd: (data) => {
dispatch(incrementAction(data));
},
handleReduce: (data) => {
dispatch(decrementAction(data));
}
}
}
// 简写形式,mapDispatchToProps的值可以是一个函数,也可以是一个返回action的对象
// const mapDispatchToProps = {
// handleAdd: incrementAction,
// handleReduce: decrementAction
// }
// 使用connect()()创建并暴露一个容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountRedux);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 异步逻辑与数据获取
在使用 redux 的时候,有时候需要进行异步操作,比如从服务端获取数据后再将这些数据存到 store 中,这时候就可以 dispatch 一个 action,这个 action 是一个函数类型的,这种我们称之为异步 action,正常情况下,我们的 action 应该是一个 object 对象,我们称之为同步 action,而在 redux 中,本身不能处理异步 action,因此,这里需要使用到一个中间件,redux-thunk
1、安装中间件
npm install redux-thunk
2、创建保存 type 值的文件和生成 action 的文件
/*
/src/redux/count/constant.js代码
*/
export const GETDATA = 'getData';
/*
/src/redux/count/count_actions.js代码
*/
import { GETDATA } from './constant'
import studentsApi from "../../api/studentsApi"; // 获取学生数据的api
// 异步获取学生数据action
export function studentAction (data) {
return {
type: GETDATA,
data
}
}
// 异步获取数据
export function getDataAction() {
return async (dispatch) => {
const response = await studentsApi.getStudents()
if (response.data) {
dispatch(studentAction(response.data.data))
}
// studentsApi.getStudents().then((res) => {
// console.log("res", res)
// // dispatch(studentAction(res.data.data))
// })
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
3、创建 countReducer.js 文件
import { GETDATA } from "./constant";
const initialState = {
students: []
}
const countReducer = (state = initialState, action) => {
switch (action.type) {
case GETDATA:
return {
...state,
students: action.data
}
default:
return state
}
}
export default countReducer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
4、在创建 store 的地方引入 redux-thunk 中间件,和执行中间件的函数 applyMiddleware
// /src/redux/store.js
import { createStore, applyMiddleware } from "redux"
import countReducer from "./count/countReducer";
import {thunk} from "redux-thunk";
export default createStore(countReducer, applyMiddleware(thunk))
2
3
4
5
6
5、创建 Students.jsx 组件,并使用 redux
import { Component } from "react";
import { connect } from "react-redux";
import { getDataAction } from "../../redux/count/count_actions";
class Students extends Component {
render() {
return (
<div>
<h3>学生信息</h3>
<ul>
{
this.props.students.map((item, index) => {
return (
<li key={index}>
姓名:{item.name},
年龄:{item.age},
性别:{item.sex}
</li>
)
})
}
</ul>
<button onClick={ () => this.props.getData() }>获取数据</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return state
}
const mapDispatchToProps = (dispatch) => {
return {
getData: () => {
dispatch(getDataAction())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Students);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# react-redux 总结
1、两个概念:
①、UI 组件:不用任何 redux 的 api,只负责页面的呈现和交互
②、容器组件:负责和 redux 通信,将结果交给 UI 组件2、如何创建容器组件
靠 react-redux 中的 connect 函数,connect (mapStateToProps, mapDispatchToProps)(UI 组件)
mapStateToProps:映射状态,返回一个对象
mapDispatchToProps:映射操作状态的方法,返回值是一个对象,mapDispatchToProps 也可以是一个对象3、容器组件中的 store 是 props 传递进去的,而不是在容器组件中直接引入的